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.

Súvisiace články