TS4.1 之前的答案;
你不能自动完成它。最大的障碍是目前无类型运算符 https://github.com/Microsoft/TypeScript/issues/16392它允许您在类型级别附加字符串文字,因此您甚至无法描述您正在执行的转换:
// without Append<A extends string, B extends string>, you can't type this:
function appendChange<T extends string>(originalKey: T): Append<T,'Change'> {
return originalKey+'Change';
}
有一个建议 https://github.com/Microsoft/TypeScript/issues/12754对于这个功能,但谁知道它是否会发生。
这意味着如果你想转换键,你需要实际硬编码你正在寻找的特定映射,从字符串文字到字符串文字:
type SomeMoreDataMapping = {
prop1: "prop1Change"
prop2: "prop2Change"
}
有了这个映射,您就可以定义这些:
type ValueOf<T> = T[keyof T]
type KeyValueTupleToObject<T extends [keyof any, any]> = {
[K in T[0]]: Extract<T, [K, any]>[1]
}
type MapKeys<T, M extends Record<string, string>> =
KeyValueTupleToObject<ValueOf<{
[K in keyof T]: [K extends keyof M ? M[K] : K, T[K]]
}>>
简要介绍:
-
ValueOf<T>
仅返回 type 的属性值类型的并集T
.
-
KeyValueTupleToObject
采用元组类型的并集,如下所示:["a",string] | ["b",number]
并将它们变成这样的对象类型:{a: string, b: number}
.
- And
MapKeys<T, M>
需要一个类型T
和键映射M
并替换任意键T
它存在于M
与相应的密钥M
。如果输入一个键T
不存在于M
,密钥未变换。如果输入一个键M
不存在于T
,它将被忽略。
现在你(终于)可以这样做:
type SomeMoreData= MapKeys<SomeData, SomeMoreDataMapping>;
如果你检查SomeMoreData
,你会看到它有正确的类型:
var someMoreData: SomeMoreData = {
prop1Change: 'Mystery Science Theater',
prop2Change: 3000
} // type checks
这应该允许您做一些有趣的事情,例如:
function makeTheChange<T>(input: T): MapKeys<T, SomeMoreDataMapping> {
var ret = {} as MapKeys<T, SomeMoreDataMapping>;
for (var k in input) {
// lots of any needed here; hard to convince the type system you're doing the right thing
var nk: keyof typeof ret = <any>((k === 'prop1') ? 'prop1Change' : (k === 'prop2') ? 'prop2Change' : k);
ret[nk] = <any>input[k];
}
return ret;
}
var changed = makeTheChange({ prop1: 'Gypsy', prop2: 'Tom', prop3: 'Crow' });
console.log(changed.prop1Change.charAt(0)); //ok
console.log(changed.prop2Change.charAt(0)); //ok
console.log(changed.prop3.charAt(0)); //ok
希望有帮助。祝你好运!
UPDATED SEP 2018 to use advantage of conditional types https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#conditional-types introduced with TS 2.8
另一个更新:如果您希望它与可选属性一起使用,它会变得更加复杂:
type RequiredKeys<T> = { [K in keyof T]-?: {} extends Pick<T, K> ? never : K }[keyof T];
type OptionalKeys<T> = { [K in keyof T]-?: {} extends Pick<T, K> ? K : never }[keyof T];
type MapKeys<T, M extends Record<string, string>> =
KeyValueTupleToObject<ValueOf<{
[K in RequiredKeys<T>]-?: [K extends keyof M ? M[K] : K, T[K]]
}>> & Partial<KeyValueTupleToObject<ValueOf<{
[K in OptionalKeys<T>]-?: [K extends keyof M ? M[K] : K, T[K]]
}>>> extends infer O ? { [K in keyof O]: O[K] } : never;
如果您需要支持索引签名,那就会变得更加复杂。
Playground 代码链接 https://www.typescriptlang.org/play/#code/C4TwDgpgBAyg9gWwgWTgJwgEQIbG87MMASwDsBzKAXigG8AoKKMNOMARgC4oAiFt9gGEAFtgoQejZqzAAmbnxmyRY8hPoBfevVCQoANWwAbAK4QA8gDMAPABUAfNSi2A2gGsIIOJecBdHeDQANKehqYQtiZgRhFw5gBGAFYQAMbAdlAQAB7AEKQAJgDOUO6e3lBiIAA0FaQgvo40DEwuQVBkzi4ADL6+3ACiOWjYaXY1rTWVDS7s-lq60ARgISCFY1DImTl5RVAASqno+daFwGhk5DWn5xT2jVIrYWaR0bEJyaNPFjbNTCVtHQOAEcTMQMPkVmsHL4ALQAfm4rS2uQKxQ8Xh8mzhG1avig3CCNVcQV6Ug0dygADIoAAFbBoYDEYzWR7GZ5RGK2OJJVLpL5Way-FoA0hQcxgRlwUjGSF2eywhH-ZE7NFlTFQbHIXH4qCEzok-xMckU7Io3ZkSwQNBijV0JUddHlcx9MXajQ60gQABuVoA3NoFvsICCwRAIZ4oY07UiHWq-PDuLR3aaVbTiCk3Osgo5sZ6fdaCVANKUMX5-YHxZLpUZZQ4nLR7aLHT5bArE8ntqi0xmszndR7vVaiyXyq3-fQyLk0JYRtB4EgcHg6FJ+BxFdcLv6mKv5FBSCYEPE-SuZABmddnTcntgAFm4+8Px60AT085Q6CwuGwTiWsrfi+wGo31QDAAKWEhbl9IA