在TypeScript中将文字联合转换为文字数组

时间:2017-04-26 09:15:57

标签: typescript

是否可以从第一种类型中提取第二种类型,所以我不必同时保留它们?

type possibleValue = "A" | "B"
type allValues = ["A", "B"]

我也愿意反过来拥有实际的对象或类,并使用typeofkeyof等提取类型信息。

我的具体问题是我有一个包含不同类型消息的消息队列。可以使用我称之为kind的一个属性(称为tagged or discriminated unions in the advanced types section in the typescript docs)来区分它们。现在我有一个函数从队列中获取其中一个消息,以及一个处理其中一个消息的函数。第一个函数采用所有kind s的数组,后者是所有消息类型的联合。我不希望这两个函数在第一个可能获得第二个无法处理的消息的情况下获得同步。

代码可能更容易理解:

interface Square {
    kind: "square";
    size: number;
}
interface Rectangle {
    kind: "rectangle";
    width: number;
    height: number;
}
interface Circle {
    kind: "circle";
    radius: number;
}

// adding a type to Shape but not KindsOfShapes would lead to a runtime
// exception I want to avoid.
type Shape = Square | Rectangle | Circle;
type KindsOfShapes = [Square["kind"], Rectangle["kind"], Circle["kind"]]

function getShape (kinds: KindsOfShapes) {
  return messageQueue.get(kinds)
}

function area(s: Shape) {
    switch (s.kind) {
        case "square": return s.size * s.size;
        case "rectangle": return s.height * s.width;
        case "circle": return Math.PI * s.radius ** 2;
    }
}

const shape = getShape(["square", "rectangle", "circle"])
const area = area(shape)

2 个答案:

答案 0 :(得分:1)

  

是否可以从第一种类型中提取第二种类型,所以我不必同时保留它们?

type possibleValue = "A" | "B"
type allValues = ["A", "B"]

没有

你可以follow the discussion here。对于可能是装饰器的奇怪解决方案,请阅读the last comment

答案 1 :(得分:0)

是的,您可以这样做。

// add an element to the end of a tuple
type Push<L extends any[], T> =
  ((r: any, ...x: L) => void) extends ((...x: infer L2) => void) ?
  { [K in keyof L2]-?: K extends keyof L ? L[K] : T } : never

// convert a union to an intersection: X | Y | Z ==> X & Y & Z
type UnionToIntersection<U> =
  (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never

// convert a union to an overloaded function X | Y ==> ((x: X)=>void) & ((y:Y)=>void)     
type UnionToOvlds<U> = UnionToIntersection<U extends any ? (f: U) => void : never>;

// convert a union to a tuple X | Y => [X, Y]
// a union of too many elements will become an array instead
type UnionToTuple<U> = UTT0<U> extends infer T ? T extends any[] ?
  Exclude<U, T[number]> extends never ? T : U[] : never : never

// each type function below pulls the last element off the union and 
// pushes it onto the list it builds
type UTT0<U> = UnionToOvlds<U> extends ((a: infer A) => void) ? Push<UTT1<Exclude<U, A>>, A> : []
type UTT1<U> = UnionToOvlds<U> extends ((a: infer A) => void) ? Push<UTT2<Exclude<U, A>>, A> : []
type UTT2<U> = UnionToOvlds<U> extends ((a: infer A) => void) ? Push<UTT3<Exclude<U, A>>, A> : []
type UTT3<U> = UnionToOvlds<U> extends ((a: infer A) => void) ? Push<UTT4<Exclude<U, A>>, A> : []
type UTT4<U> = UnionToOvlds<U> extends ((a: infer A) => void) ? Push<UTT5<Exclude<U, A>>, A> : []
type UTT5<U> = UnionToOvlds<U> extends ((a: infer A) => void) ? Push<UTT6<Exclude<U, A>>, A> : []
type UTT6<U> = UnionToOvlds<U> extends ((a: infer A) => void) ? Push<UTT7<Exclude<U, A>>, A> : []
type UTT7<U> = UnionToOvlds<U> extends ((a: infer A) => void) ? Push<UTT8<Exclude<U, A>>, A> : []
type UTT8<U> = UnionToOvlds<U> extends ((a: infer A) => void) ? Push<UTT9<Exclude<U, A>>, A> : []
type UTT9<U> = UnionToOvlds<U> extends ((a: infer A) => void) ? Push<UTTX<Exclude<U, A>>, A> : []
type UTTX<U> = []; // bail out

type Test = UnionToTuple<"a | "b"> // ["a", "b"]