![](/img/trans.png)
[英]How to to make object property typed to be a string from another property's array of strings in TypeScript
[英]How to make typed object map from array of pairs?
假设我有一组这样的对:
const attributes = [
['Strength', 'STR'],
['Agility', 'AGI'],
['Intelligence', 'INT']
// ..
] as const;
我想生成类似的东西:
const attributesMap = {
Strength: {
name: 'Strength',
shortName: 'STR',
},
Agility: {
name: 'Agility',
shortName: 'AGI',
},
// ..
};
它应该是完全输入的,所以如果我想访问attributesMap.Strength.shortName
我会知道它将是"STR"
这个怎么打?
我想出的最好的就是这个,但它并没有按预期工作,因为只有键被输入为 const 值, shortName
仍然是所有值的联合,我不知道如何使它成为非联合值:
type mapType = {
[P in typeof attributes[number][0]]: {
name: P;
shortName: typeof attributes[number][1];
};
};
你快到了:
type mapType = {
[P in typeof attributes[number][0]]: {
name: P;
// Wrap with Extract here
shortName: Extract<typeof attributes[number], readonly [P, unknown]>[1];
};
};
结果是
type mapType = {
Strength: {
name: "Strength";
shortName: "STR";
};
Agility: {
name: "Agility";
shortName: "AGI";
};
Intelligence: {
name: "Intelligence";
shortName: "INT";
};
}
@A_Blop 的回答是正确的; 我只是想跟进一个使用 TypeScript 4.1 的密钥重新映射功能作为使用Extract
的替代方法的版本。 鉴于:
const attributes = [
['Strength', 'STR'],
['Agility', 'AGI'],
['Intelligence', 'INT']
// ..
] as const;
让我们给attributes
的类型一个我们以后可以使用的名称,为了使重新映射更容易,让我们对其进行限制,使其只有每个元素的数字文字键(即0
、 1
、 2
),而不是任何Array
像push
, pop
等键:
type Atts = Omit<typeof attributes, keyof any[]>;
/* type Atts = {
readonly 0: readonly ["Strength", "STR"];
readonly 1: readonly ["Agility", "AGI"];
readonly 2: readonly ["Intelligence", "INT"];
} */
有了Atts
,我们现在可以使用as
子句进行重新映射。 对于Atts
中的每个键I
,类型Atts[I][0]
是键/ name
,类型shortName
Atts[I][1]
是短名称:
type MappedAttributes = {
-readonly [I in keyof Atts as Atts[I][0]]: {
name: Atts[I][0],
shortName: Atts[I][1]
}
};
/*
type MappedAttributes = {
Strength: {
name: "Strength";
shortName: "STR";
};
Agility: {
name: "Agility";
shortName: "AGI";
};
Intelligence: {
name: "Intelligence";
shortName: "INT";
};
}
*/
(我也删除了readonly
,但如果你不想这样做,你不必这样做)。 这就是你想要的类型!
您可以使用递归类型:
type MapType<T extends readonly (readonly [string, unknown])[]> =
// if the list can be split into a head and a tail
T extends readonly [readonly [infer A, infer B], ...infer R]
// these should always be true but typescript doesn't know that
? A extends string ? R extends readonly (readonly [string, unknown])[]
? {
// this'll be just one property
[K in A]: {
name: A,
shortName: B
}
// we intersect with the type recursed on the tail
} & MapType<R>
// these should never happen
: never : never
// base case, empty object
: {};
或者,为了避免重复readonly
,您可以创建一个递归地摆脱它的类型:
type RemoveReadonly<T> = T extends readonly unknown[] ? {
-readonly [K in keyof T]: RemoveReadonly<T[K]>;
} : T;
type MapType<T extends [string, unknown][]> =
// if the list can be split into a head and a tail
T extends [[infer A, infer B], ...infer R]
// these should always be true but typescript doesn't know that
? A extends string ? R extends [string, unknown][]
? {
// this'll be just one property
[K in A]: {
name: A,
shortName: B
}
// we intersect with the type recursed on the tail
} & MapType<R>
// these should never happen
: never : never
// base case, empty object
: {};
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.