Tipos de letra mecanografiada, genéricos y clases abstractas
Experimento un comportamiento que me parece extraño.
Consideremos la siguiente muestra (pruébalo en el patio de juegos mecanografiado):
abstract class FooAbstract {
abstract bar() {}
}
class Foo extends FooAbstract {
bar() {
return { bar: 'bar' };
}
}
class FooMaker<FOO extends FooAbstract> {
constructor(public foo: FOO) {}
bar() {
return this.foo.bar();
}
baz = () => {
return this.foo.bar();
}
}
let foo = new Foo();
let result = foo.bar();
let foomaker = new FooMaker(new Foo);
let foo2 = foomaker.foo; // Type "Foo", OK
let result1 = foomaker.foo.bar(); // Type "{bar: string}", OK
let result2 = foomaker.bar(); // Type "{}", ???
let result3 = foomaker.baz(); // I've seen comments about using a lambda... Not better
result2
yresult3
se escriben como el resumenbar
función ({}
) Parece quethis
no se resuelve como la clase concretaFoo
pero como la clase abstractaFooAbstract
. Mientras que el tipo defoo2
muestra que la clasefoo
La propiedad se resuelve correctamente.
Que esta pasando? ¿Hago algo de manera incorrecta?
actualizarComo una ocurrencia tardía, este caso puede reformularse así (Pruébelo en el campo de juegos de Typecript):
class Foo {
bar() {
return { bar: 'bar' };
}
getThis(): this {
return this
}
}
class Wrapper {
bar<FOO extends { bar(): {} }>(foo:FOO) {
return foo.bar();
}
}
let wrapper = new Wrapper();
let result = (new Foo()).bar();
let result2 = wrapper.bar(new Foo());
result
tiene el tipo{bar:string}
result2
tiene el tipo{}
(desde la interfaz).wrapper.bar
tiene el tipoWrapper.bar<Foo>(foo: Foo): {}
Con esta muestra, es más claro que, incluso sabiendo queFOO
se escribe comoFoo
, Mecanografiado usosFOO
definición y no su tipo explícito comobar
tipo de retorno
Ok, mientras peleaba con tipings, creo que subí de nivel. El concepto es, de hecho, que las tipificaciones implícitas enMecanografiado no siga ningún modelo de herencia incluso cuando se deduce un tipo. Bueno, todavía me preguntopor qué ova a cambiar, pero tendré que lidiar con "es así". Entonces, en este caso, el tipo debe ser explícito.
Encontré una manera más simple de escribir su ejemplo (pruébalo en el campo de juegos Typecript):
abstract class FooAbstract {
abstract bar(): {}
}
class Foo extends FooAbstract {
bar() {
return { bar: 'bar' };
}
}
class FooMaker<FOO extends FooAbstract, BAR> {
constructor(public foo: FOO & { bar: () => BAR } ) {
}
bar():BAR {
return this.foo.bar() as BAR;
}
}
let foomaker = new FooMaker(new Foo());
let result = foomaker.bar();
result
obtiene el tipo{bar:string}
y no hay necesidad de poner genéricos en todas partes. Las cosas en elFooMaker.constructor
El tipo de parámetro podría ser más limpio al referir una interfaz con un genérico.