TypeScript - Förhindra typinferens i klasskonstruktorn


Titta på detta exempel:

class Foo<T extends string> {
    constructor(bar: T) { }
}

const a = new Foo("a"); // type => Foo<"a">
const b = new Foo("b"); // type => Foo<"b">

I koden ovan kan du se att variablerna a och b har härledda typer från konstruktören, men jag skulle vilja att de båda har bokstavliga typer som jag själv anger ELLER återfall till standardvärdet som är Foo<string>. Efter att ha experimenterat kom jag fram till denna lösning:

class Foo<T extends string> {
    constructor(bar: T extends string ? T : never) { }
}

const a = new Foo("a"); // type => Foo<string>
const b = new Foo("b"); // type => Foo<string>
const c = new Foo<"a" | "b" | "c">("c"); // type => Foo<"a" | "b" | "c">
const d = new Foo<"a">("c"); // error => [ts] Argument of type '"c"' is not assignable to parameter of type '"a"'. [2345]

d ger ett fel som det ska göra när typerna inte stämmer och det är precis vad jag behöver.

Vad jag vill veta är, varför gör

T extends string ? T : never

förhindra härledning? Det kokar i princip ner till true ? T : never vilket vidare kokar ner till T.

Redigera 1: Det kan förenklas ytterligare:

class Foo<T extends string> {
    constructor(bar: T extends T ? T : never) { }
}

Redigera 2: KeepType verkar fungera med vad som helst så länge det angivna T inte är utökat (standardtypen kommer att vara vad du väljer och du kommer fortfarande få ett fel om du gör fel):

type KeepType<T> = T extends T ? T : never; // Only works when T is not extending anything

class Foo<T = unknown> {
    constructor(bar: KeepType<T>) { }
}

const a = new Foo("a"); // type => Foo<unknown>
const b = new Foo("b"); // type => Foo<unknown>
const c = new Foo<"a" | "b" | "c">("c"); // type => Foo<"a" | "b" | "c">
const d = new Foo<"a">("c"); // error => [ts] Argument of type '"c"' is not assignable to parameter of type '"a"'. [2345]