Reputation: 38431
Im expecting the arg
param to have inferred type from parent class
export abstract class IEngineClas {
abstract viewer(arg: string): boolean
}
export class MyClass extends IEngineClas {
viewer(arg) {
return true
}
}
However in practice compiler complains that arg
has implicit type of any.
I also tried approach with an interface
export interface IEngine {
viewer?: (arg: string) => boolean
}
export class MyClass implements IEngine {
viewer(arg) {
return true
}
}
It has the same issue with compiler thinking arg
has type of any.
Why is type-inference not working here? And what can I do to get it to work?
Upvotes: 14
Views: 2689
Reputation: 250256
There isn't a good way to do this. The closest and simplest you are going to get is if you implement
an interface (or a class, the abstract class can be implemented as well, but you loose shared code) is to use a function field instead and type it as :
export abstract class IEngineClas {
abstract viewer(arg: string): boolean
}
export class MyClass implements IEngineClas {
viewer: IEngineClas['viewer'] = function (this: MyClass, arg) { // we need to be explicit about who this is
return true;
}
}
This has the disadvantage that the function is assigned to each instance not to the prototype, you also use super
.
If you feel strongly about this, I'd come up with a proposal on the GitHub project.
Upvotes: 3
Reputation: 3273
Of course you can infer!
Typescript has the most powerful generic system ever seen!
It just takes some mystic syntax.
You may write that (check it on Typescript Playground) :
export abstract class IEngineClas {
abstract viewer(arg: string): boolean
}
export class MyClass extends IEngineClas {
viewer(arg: IEngineClas["viewer"] extends (arg: infer U) => any ? U : any) {
return true
}
}
let test = (new MyClass()).viewer("hop") // Type OK
let test2 = (new MyClass()).viewer(1) // Wrong type
IEngineClas["viewer"]
can retrieve the type of your parent function: (arg:string) => boolean
Using the conditional types, you can retrieve the arg you want by using the infer keyword to assign it to a generic.
Read it like this: if the type of IEngineClas["viewer"]
is (arg: U) => any
(a function with an argument), grab the type of U
(the first argument) and use it as the type of the parameter arg
. Otherwise, use the type any
.
A better way to write it, with a type (check it on Typescript Playground):
type firstArg<T> = T extends (arg: infer U) => any ? U : any
export abstract class IEngineClas {
abstract viewer(arg: string): boolean
}
export class MyClass extends IEngineClas {
viewer(arg: firstArg<IEngineClas["viewer"]>) {
return true
}
}
let test = (new MyClass()).viewer("hop") // Type OK
let test2 = (new MyClass()).viewer(1) // Wrong type
On a different case, I asked one day why these expected inference behaviors concerning the abstract classes weren't the default, and was answered it was due to performance issues. And I admit that on big projects, Typescript becomes excessively slow. Even if a compilation flag to activate the typings or not on the abstract classes would have been welcomed.
The post where I asked: https://github.com/Microsoft/TypeScript/issues/21428
If you just want to get rid of the implicit any warning, just specify the any
type explicitly: viewer(arg:any)
, or disable the noImplicitAny flag in your compiler options.
Upvotes: 8
Reputation: 223064
There is no type inference here. Member types aren't inferred by parent class or implemented interface. arg
is not inferred string
but implicit any
.
Child class has a chance to override method signature, as long as it's compatible with parent method. It's possible to define the method as viewer(arg: any) {...}
. Since string
is a subset of any,
this will be allowed, while viewer(arg: boolean) {...}
won't.
viewer(arg) {...}
results in implicit any
for arg
, it's same as viewer(arg: any) {...}
. It will work in loose compiler mode but will result in type error with strict
or noImplicitAny
compiler option. noImplicitAny
is particularly helpful to avoid accidental inferred any
in situations like this one.
Upvotes: 7