Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/typescript/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/webpack/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Typescript 如何从对象的元组创建对象类型?_Typescript_Tuples_Mapped Types - Fatal编程技术网

Typescript 如何从对象的元组创建对象类型?

Typescript 如何从对象的元组创建对象类型?,typescript,tuples,mapped-types,Typescript,Tuples,Mapped Types,从该数据结构中: const属性=[ {name:'name',类型:'}, {name:'age',类型:0}, {name:'sex',键入:['m','f']as const}, {name:'username',类型:''} ] 我正在尝试动态构造以下类型: type Person={ 名称:字符串; 年龄:人数; 性别:'m'|'f'; 用户名:字符串; } 我知道TypeScript可以通过映射其他类型来创建类型。但是这里的源对象是一个数组,所以看起来有点棘手。我该怎么做呢?这其

从该数据结构中:

const属性=[
{name:'name',类型:'},
{name:'age',类型:0},
{name:'sex',键入:['m','f']as const},
{name:'username',类型:''}
]
我正在尝试动态构造以下类型:

type Person={
名称:字符串;
年龄:人数;
性别:'m'|'f';
用户名:字符串;
}

我知道TypeScript可以通过映射其他类型来创建类型。但是这里的源对象是一个数组,所以看起来有点棘手。我该怎么做呢?

这其实没那么难。您所需要的只是一个映射类型,其中的键被重新映射到元组成员的
name
属性的类型(这可以很好地实现)以及相应成员的
type
属性的值

首先,您需要让编译器知道
属性
数组实际上是一个元组,具有
as const
断言:

const属性=[
{name:'name',类型:'},
{name:'age',类型:0},
{name:'sex',键入:['m','f']as const},
{name:'username',类型:''}
]作为常量;
接下来,让我们定义一个能够对元组成员的任意属性进行操作的泛型类型,从而使该类型可重用。首先,我们需要提取能够映射的索引 成员以一对一的方式访问属性。这是通过
排除
(仅保留索引)完成的

我们可以使用生成的索引并集进行映射:

类型TupleToProps={
[P作为T[P][VP]和字符串排除在外]:T[P][VV]
};
VP
VV
确保实用程序类型可以处理任何同质的对象元组,并且
&string
只保留与字符串兼容的属性。这已经给我们带来了一个非常好的结果:

type PersonTest=TupleToProps;
/**
*类型PersonTest={
*姓名:”;
*年龄:0岁;
*性别:只读[“m”,“f”];
*用户名:“”;
* }
*/
在此之后,这只是一个表面上的变化:从类似元组的类型中提取值并扩大文本类型(由和Q&a提供,并进行了一个小的升级,以排除不应扩大的键):

键入ToPrimitive=
T扩展字符串?一串
:T扩展数字?数
:T扩展布尔值?布尔值
:T;
//将保留具有更宽值类型的键的映射类型
类型加宽={
[K in keyof O]:K扩展E?O[K]:ToPrimitive
}
类型TupleToPropsTwo=加宽;
类型PersonTest2=TupleToPropsTwo;
/**
*类型PersonTest2={
*名称:字符串;
*年龄:人数;
*性别:“m”|“f”;
*用户名:字符串;
* }
*/

映射类型确实是一条可行之路。为确保没有类型被加宽,您必须将
作为常量添加到
属性中

const properties = [
  { name: 'name', type: '' },
  { name: 'age', type: 0 },
  { name: 'sex', type: ['m', 'f'] as const },
  { name: 'username', type: '' }
] as const
然后,您可以使用映射类型,在该类型中,您可以将键从数组索引重新映射到属性名称,并将值映射到类型。映射类型本身如下所示:

type FromProperties<P extends readonly unknown[]> = {
  [K in IndexKeys<P> as Name<P[K]>]: Type<P[K]>
} 
要获取名称,可以使用简单的条件类型:

type Name<O> = O extends { name: infer N } ? N extends string ? N : never : never
extends子句用于防止推断太窄的类型(例如,
'
而不是
string
),并用于推断数组的联合类型。如果需要,可以添加更多类型

有了这些定义,
属性
可以用

type Person = FromProperties<typeof properties>
// Inferred type:
// type Person = {
//     name: string;
//     age: number;
//     sex: "m" | "f";
//     username: string;
// }
type Person=FromProperties
//推断类型:
//类型人={
//名称:字符串;
//年龄:人数;
//性别:“m”|“f”;
//用户名:字符串;
// }

我认为您必须编写一个脚本,将
属性
数组转换为类型声明,并将其放入.d.ts文件中。
type Type<O> = 
  O extends { type: infer T }
  ?   T extends number ? number 
    : T extends string ? string 
    : T extends readonly unknown[] ? T[number]
    : never // `name` property is not a number, string or array
  : never // object has no `type` property
type Person = FromProperties<typeof properties>
// Inferred type:
// type Person = {
//     name: string;
//     age: number;
//     sex: "m" | "f";
//     username: string;
// }