Typescript Generisk Typepåstand


Så her er en oppsummering av mine observasjoner når jeg jobber med TypeScript.

Her er litt kode:

type someTypeEnum = '1';
type someOtherTypeEnum = '2' | '3';
type combinedTypeEnum = someTypeEnum | someOtherTypeEnum;

Her er den første saken :-

function typeAssertion<T extends combinedTypeEnum>(args: T): args is someTypeEnum {
    // The error i get
    // A type predicate's type must be assignable to its parameter's type.
    //  Type '"1"' is not assignable to type 'T'.
    return undefined;
}

Jeg kan ikke forstå hvorfor denne tingen feiler fordi vi allerede har begrenset argumentene våre til combinedTypeEnum, i tilfelle vi gjør det

typeAssertion('4')

Vi får allerede en feil som sier '4' ikke er et gyldig argument, så hvorfor er det at args is someTypeEnum anses som en ugyldig predikat.

Her er den andre saken :-

function typeAssertion(args: combinedTypeEnum): args is someTypeEnum {
    return undefined;
}

Dette ser ut til å fungere fint, men i tilfelle vi gjør dette :-

function someFunction<T extends combinedTypeEnum>(args: T): T {
    if (typeAssertion(args)) {
        // args here is  'T & "1"' 
        args
    }
    return args
};

hvorfor er det slik at vi har T & "1" og ikke bare "1", vi fastslo spesifikt at det er someTypeEnum.

Jeg var virkelig nysgjerrig på hvorfor slike beslutninger ble tatt. Det ville være veldig nyttig å se hvordan ting går galt i tilfelle ting ble gjort på en annen måte.

Svar #1

extends gir ikke mye mening når du har streng-litteraler. For å gjøre forklaringen enklere, la meg bruke andre typer. Vurder disse tre klassene:

class Animal {}

class Dog extends Animal {}

class Cat extends Animal {}

når vi bruker generics, blir den faktiske typen satt av kalleren:

function foo<T extends Animal>(arg: T) {}

foo(new Dog()); //T is Dog, equivalent to foo(arg: Dog) {}
foo(new Cat()); //T is Cat, equivalent to foo(arg: Cat) {}

Nå kan du kanskje allerede se hvor vi er på vei. La oss bruke en typepredikat:

function foo<T extends Animal>(arg: T): arg is Cat {}

Når vi kaller foo(new Dog()) blir det siste eksemplet tilsvarende dette:

function foo(arg: Dog): arg is Cat {}

Og selvfølgelig fungerer det ikke eller gir mening.

Når det gjelder ditt andre eksempel: Typen til variabelen endres ikke. Poenget er at ved å angi en spesifikk type lar kompilatoren deg gjøre alt som kan gjøres med denne typen.

Svar #2

OPPDATERING :

Eller mye enklere:

function typeAssertion(args: combinedTypeEnum): args is someTypeEnum {
    return args === "1";
}

Se denne lekeplassen


Original :

Dette hadde meg fastlåst en god stund også. Faktisk er løsningen (i det minste i 2021, ikke sikker på om det var tilbake da spørsmålet ble stilt) slik:

function typeAssertion<T extends combinedTypeEnum>(args: T): args is T & someTypeEnum {
    return args === "1";
}

Ideen bak dette (så vidt jeg forstår det fra dette svaret) er dette: når du kaller typeAssertion("2"), får T verdien "2" (literal type "2"), noe som betyr at du ender opp med funksjonen:

function typeAssertion(args: "2"): args is someTypeEnum

som åpenbart ikke gir mening. Jeg er ikke sikker på om løsningen (med T &) gir mer mening, men det fungerer:

type someTypeEnum = '1';
type someOtherTypeEnum = '2' | '3';
type combinedTypeEnum = someTypeEnum | someOtherTypeEnum;

function typeAssertion<T extends combinedTypeEnum>(args: T): args is T & someTypeEnum {
    return args === "1";
}

const a: combinedTypeEnum = "1"
const b: combinedTypeEnum = "2"
const c: combinedTypeEnum = "3"
const d = "1"
const e = "2"
const f = "4"

let one: "1" = "1"

if (typeAssertion(a)) one = a
if (typeAssertion(b)) one = b
if (typeAssertion(c)) one = c
if (typeAssertion(d)) one = d
if (typeAssertion(e)) one = e
if (typeAssertion(f)) one = f // this one gives an error

Se i lekeplassen

Relaterte artikler