Versão TypeScript: 2.2.2
Posso estar entendendo mal como os genéricos funcionam (se sim, por favor me indique a direção certa), mas isso parece um bug.
Código
// A *self-contained* demonstration of the problem follows...
interface BaseState {
on: boolean;
};
class Component {
state: BaseState;
setState(state: BaseState) {
this.state = state;
}
onInput({ value }: { value: number }) {
this.setState({ on: value > 0 }); // no error
}
}
class GenericComponent<State extends BaseState> {
state: State;
setState(state: State) {
this.state = state
}
onInput({ value }: { value: number }) {
this.setState({ on: value > 0 }); // error Argument of type '{ on: boolean; }' is not assignable to parameter of type 'State'.
}
}
Comportamento esperado:
Na classe Component
do exemplo acima não causa erros ao usar a interface BaseState
diretamente. Espero o mesmo comportamento da classe GenericComponent
uma vez que State
genérico estende explicitamente BaseState
.
Comportamento real:
Em vez disso, recebo o seguinte erro relacionado a this.setState({ on: value > 0 });
: "Argumento do tipo '{on: boolean;}' não pode ser atribuído ao parâmetro do tipo 'Estado'."
Considere o que acontece se você escrever isto
var t = new GenericComponent<{on: boolean, thing: string}>();
t.onInput({ value: 30 });
t.state.thing.substr(0); // 'thing' property should exist, but doesn't
Aqui está um exemplo simplificado:
class Stateful<State extends { on: boolean }> {
state: State = {
on: true
}; // error
}
O motivo pelo qual isso não funciona é que só será válido quando Stateful
for instanciado com um argumento de tipo precisamente equivalente a { on: boolean }
.
Basicamente, _existe_ um tipo T
, satisfazendo as restrições do argumento de tipo Stateful
, de modo que a instanciação é válida, mas isso não se aplica _para todos os_ tipos T
onde T
satisfaz a restrição do argumento de tipo de Stateful
.
Portanto, o código errôneo acima afirma que, para todos os tipos T<T>
onde T,
Stateful
Logicamente, podemos olhar para isso da seguinte maneira
Afirmação
P
o tipo { on: boolean }
T
forma que T
seja atribuível a P
, a definição de Stateful<T>
acima é válidaRejeição por contra-exemplo
U
o tipo { on: boolean, value: number }
U
ser atribuído a P
-> por definiçãoStateful<U>
é inválido -> por substituiçãoT
tal que T
ser atribuído a P
e Stateful<T>
é uma instanciação inválida -> por implicação@RyanCavanaugh @aluanhaddad Droga, eu sabia disso. Fiquei confuso enquanto tentava resumir o problema a algo mais simples. Então, vamos tentar de novo. E neste caso, usar um Partial? Ainda recebo um erro semelhante.
interface BaseState {
on: boolean;
};
class Component {
state: BaseState;
setState(partialState: Partial<BaseState>) {
this.state = { ...this.state, ...partialState };
}
onInput({ value }: { value: number }) {
this.setState({ on: value > 0 }); // no error
}
}
class GenericComponent<State extends BaseState> {
state: State;
setState(partialState: Partial<State>) {
this.state = { ...this.state, ...partialState };
}
onInput({ value }: { value: number }) {
this.setState({ on: value > 0 }); // error: Argument of type '{ on: boolean; }' is not assignable to parameter of type 'Partial<State>'
}
}
Comentários muito úteis
Considere o que acontece se você escrever isto