Odvodenie typov v triedach TypeScriptu
Očakávam, že parameter arg bude mať odvodený typ z nadradenej triedy
export abstract class IEngineClas {
abstract viewer(arg: string): boolean
}
export class MyClass extends IEngineClas {
viewer(arg) {
return true
}
}
Avšak v praxi kompilátor hlási, že arg má implicitný typ any.
Skúsil som aj prístup s rozhraním
export interface IEngine {
viewer?: (arg: string) => boolean
}
export class MyClass implements IEngine {
viewer(arg) {
return true
}
}
Má rovnaký problém s tým, že kompilátor si myslí, že arg má typ any.
Prečo tu nefunguje odvodzovanie typu? A čo môžem urobiť, aby to fungovalo?
Odpoveď č. 1
Samozrejme, že môžete odvodiť! Typescript má najsilnejší generický systém, aký bol kedy vidieť!
Stačí len trochu mystickej syntaxe.
Môžete napísať toto (skontrolujte to na 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
Vysvetlenie:
IEngineClas["viewer"] môže získať typ vašej nadradenej funkcie: (arg:string) => boolean
Použitím podmienených typov môžete získať argument, ktorý chcete, použitím kľúčového slova infer na jeho priradenie k generiku.
Prečítajte si to takto: ak je typ IEngineClas["viewer"] (arg: U) => any (funkcia s argumentom), vezmite typ U (prvý argument) a použite ho ako typ parametra arg. V opačnom prípade použite typ any.
Upraviť
Lepší spôsob, ako to napísať, s typom (skontrolujte to na 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
Dôvod
V inom prípade som sa jedného dňa spýtal, prečo tieto očakávané správanie inferencie týkajúce sa abstraktných tried nebolo predvolené, a odpovedali mi, že je to kvôli problémom s výkonom. A priznávam, že pri veľkých projektoch sa Typescript stáva nadmerne pomalým. Dokonca by bolo vítané, keby existoval kompilátorový parameter na aktiváciu typov alebo nie pri abstraktných triedach.
Príspevok, kde som sa pýtal: https://github.com/Microsoft/TypeScript/issues/21428
Ah, a...
Ak sa len chcete zbaviť upozornenia implicit any, jednoducho zadajte typ any explicitne: viewer(arg:any), alebo vypnite vo svojich možnostiach kompilátora príznak noImplicitAny.
Odpoveď č. 2
Tu nie je žiadna inferencia typov. Typy členov nie sú odvodené z nadradenej triedy alebo implementovaného rozhrania. arg nie je odvodený string, ale implicitný any.
Potomkovská trieda má možnosť prepísať podpis metódy, pokiaľ je kompatibilný s metódou rodiča. Je možné definovať metódu ako viewer(arg: any) {...}. Keďže string je podmnožinou any, toto bude povolené, zatiaľ čo viewer(arg: boolean) {...} nebude.
viewer(arg) {...} vedie k implicitnému any pre arg, je to rovnaké ako viewer(arg: any) {...}. Bude to fungovať v režime voľného kompilátora, ale spôsobí to chybu typu pri kompilátorových možnostiach strict alebo noImplicitAny. noImplicitAny je obzvlášť užitočné na zabránenie náhodného odhadnutia any v situáciách ako táto.
