typescript - Hur skapar jag en diskriminerad unionstyp från en registertyp?


Är det möjligt att skapa en mappad|villkorlig typ så att vilken typ som helst såsom

interface Car {
    color: string
    max_speed: number
    doors: number
}

type CarPorperties = SomeMappedAndConditiomalType<Car>

kommer att resultera i

type CarProperties =
    {color: string}
    |{max_speed: number}
    |{doors: number}

Svar #1

Det visar sig vara möjligt: mappa egenskapstyperna till något som { __tag: k, [k]: T[k] }, och ta sedan unionen av dessa typer. Detta ger en taggad unionstyp där egenskapen __tag är diskriminanten.

Exempel på användning:

interface Car {
    color: string
    max_speed: number
    doors: number
}

type Test = ObjectToTaggedUnion<Car>

/*
 * Test = { __tag: 'color', color: string }
 *      | { __tag: 'max_speed', max_speed: number }
 *      | { __tag: 'doors', doors: number }
 */ 

Implementeringen nedan. Detaljerna är lite knepiga eftersom Typescript inte gör det enkelt att ha en objekttyp där egenskapen __tag heter bokstavligen men egenskapen k inte gör det.

type ObjectToTaggedUnion<T> = {
    [K in keyof T]: K extends string
        ? { [kk in K | '__tag']: kk extends '__tag' ? K : T[K] }
        : never
}[keyof T];

Lekplatslänk

Svar #2

Det du vill göra är att dela produkttypen i summatyp. Jag tror att det inte är möjligt, du kan ta nycklar och värden separat men att ha par verkar inte uppnåeligt.

Men vi kan gå en annan väg. Så vi kan börja från typer som representerar varje egenskap och kombinera dem med & och |

// single props types
type ColorProp = { color: string }
type MaxSpeedProp = { max_speed: number }
type DoorsProp = { doors: number }

type Car = ColorProp & MaxSpeedProp & DoorsProp // product
type CarProps = ColorProp | MaxSpeedProp | DoorsProp // sum

// we can build Car directly as it would be orginal interface
const car: Car = {
  color: 'color',
  max_speed: 1,
  doors: 2
}

// or create it by merging props types instances
const doors: DoorsProp = { doors: 2 };
const max_speed: MaxSpeedProp = { max_speed: 2 };
const color: ColorProp = { color: 'red' };

const car2: Car = {
  ...doors,
  ...color,
  ...max_speed
}

På det sättet kan du komponera mindre typer - propstyper till större kompositer - summor och produkter.

Relaterade artiklar