generics - TypeScript diskriminerad unionstyp med standardvärde och typinferens


Jag vill skapa en diskriminerad unionstyp, där det inte är nödvändigt att ange diskriminatorvärdet.

Här är min nuvarande kod:

interface Single<T> {
  multiple?: false // this is optional, because it should be the default
  value: T
  onValueChange: (value: T) => void
}

interface Multi<T> {
  multiple: true
  value: T[]
  onValueChange: (value: T[]) => void
}

type Union<T> = Single<T> | Multi<T>

För testning använder jag detta:

function typeIt<T>(data: Union<T>): Union<T> {
    return data;
}

const a = typeIt({ // should be Single<string>
    value: "foo",
    onValueChange: (value) => undefined // why value is of type any?
})

const b = typeIt({ // should be Single<string>
    multiple: false,
    value: "foo",
    onValueChange: (value) => undefined
})

const c = typeIt({ // should be Multi<string>
    multiple: true,
    value: ["foo"],
    onValueChange: (value) => undefined
})

Men jag får en massa fel och varningar...:

  1. I const a's onValueChange är typen av parametern value any. När man sätter multiple: false uttryckligen (som i const b) härleds den korrekt som string.

  2. const c fungerar inte alls. Jag får detta fel: "Typen 'string' kan inte tilldelas typen 'string[]'".

Har du någon aning om hur man löser det här?

Jag har skapat en TypeScript Playground med denna kod

Svar #1

Jag tror inte att kompilatorn enkelt kan härleda typen av value-parametern i callbacken eftersom typen av objektliteralen fortfarande inte är bestämd när callbacken kontrolleras.

Om du inte har många fackmedlemmar är en lösning som fungerar som förväntat att använda flera överlagringar:

export interface Single<T> {
  multiple?: false // this is optional, because it should be the default
  value: T
  onValueChange: (value: T) => void
}

interface Multi<T> {
  multiple: true
  value: T[]
  onValueChange: (value: T[]) => void
}

type Union<T> = Single<T> | Multi<T>

function typeIt<T>(data: Single<T>): Single<T>
function typeIt<T>(data: Multi<T>): Multi<T>
function typeIt<T>(data: Union<T>): Union<T> {
    return data;
}

const a = typeIt({ // is Single<string>
    value: "foo",
    onValueChange: (value) => undefined // value is typed as expected
})

const b = typeIt({ // is Single<string>
    multiple: false,
    value: "foo",
    onValueChange: (value) => undefined
})

const c = typeIt({ // is be Multi<string>
    multiple: true,
    value: ["foo"],
    onValueChange: (value) => undefined
})

Relaterade artiklar