简体   繁体   English

TypeScript 中的映射类型

[英]Mapping types in TypeScript

Is it possible in TS to transform this type TS中是否可以转换这种类型

type input = {
    a: 1;
    b: 2;
    c: 3;
};

into this type成这种类型

type output = { a: 1 } | { b: 2 } | { c: 3 };

? ?

You want a type function that converts Input to Output ;您需要一个将Input转换为Output的类型函数; let's call it " Split<T> ", for want of a better name.让我们称它为“ Split<T> ”,因为需要一个更好的名字。 Here's one approach:这是一种方法:

type Split<T extends object> =
  { [K in keyof T]-?: Pick<T, K> }[keyof T];

On the inside we're using the Pick<T, K> utility type to represent the same type as T but only with the keys from K .在内部,我们使用Pick<T, K>实用程序类型来表示与T相同的类型,但仅使用来自K的键。 So Pick<Input, "a"> is {a: 1} :所以Pick<Input, "a">{a: 1}

type Test = Pick<Input, "a">
// type Test = { a: 1; }

On the outside is a distributive object type (as coined in microsoft/TypeScript#47109 ), which is what you get when you make a mapped type over some set of keys and then immediately index into it with the same set of keys.外部是分布式对象类型(如microsoft/TypeScript#47109中所创造的),当您在一组键上创建映射类型,然后立即使用同一组键对其进行索引时,您会得到这种类型。 I

Distributive object types let you take a type function F<K> for some keylike type K , and produce the union of F<K> for each union member K in some set of keys.分布式对象类型让您可以为某些类似键的类型K采用类型函数F<K> ,并为某些键集中的每个联合成员K生成F<K>的联合。 In the above case, keyof T is the set of keys.在上述情况下, keyof T是键的集合。 So the above will compute the union of Pick<T, K> for each K in keyof T , which is what you want:因此,上面将为keyof T中的每个K计算Pick<T, K>的并集,这就是您想要的:

type Input = {
  a: 1;
  b: 2;
  c: 3;
};

type Output = Split<Input>;
// type Output = Pick<Input, "a"> | Pick<Input, "b"> | Pick<Input, "c">

If you don't like seeing Pick in the IntelliSense output, you can inline the definition of Pick<T, K> into Split<T> :如果您不喜欢在 IntelliSense 输出中看到Pick ,您可以将Pick<T, K>的定义内联到Split<T>中:

type Split<T extends object> =
  { [K in keyof T]-?: { [P in K]: T[P] } }[keyof T];

type Output = Split<Input>;
// type Output = { a: 1; } | { b: 2; } | { c: 3; }

Playground link to code Playground 代码链接

You can do this:你可以这样做:

type input = {
a: 1;
b: 2;
c: 3;
};

type output = { a: 1 } | { b: 2 } | { c: 3 };

let i : input = {
    a: 1,
    b: 2,
    c: 3
};

let o : output;

let {a, b, c} = i;

o = {a} || {b} || {c};

Honestly, I have no idea how to implement this from scratch.老实说,我不知道如何从头开始实现它。 But here's a library that does exactly this and much more (ts-toolbelt):但这里有一个库可以做到这一点以及更多(ts-toolbelt):

import { Object } from 'ts-toolbelt';

type Input = {
  a: 1;
  b: 2;
  c: 3;
};

type Output = Object.Either<Input, keyof Input>;

Not sure if this scales to more complicated input types, might get weird with nested things不确定这是否可以扩展到更复杂的input类型,嵌套的东西可能会变得很奇怪

type input = {
    a: 1;
    b: 2;
    c: 3;
};

// given key K, construct {[K]: T[K]}
type m2<T, K extends keyof T> = {[k in K]: T[k]}

// for each key K in T, construct {[K]: T[K]}, and union the results
type m1<T, K extends keyof T> = K extends any ? m2<T, K> : never;

type map<T> = m1<T, keyof T>;
type result = map<input>

declare const result : result;
if ("a" in result) {
    result.a // type 1
}
else if ("b" in result) {
    result.b; // type 2
}
else {
    result.c // type 3
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM