我试图为我的对象Record<string, string | number, {} | []>
创建typescripttype
,如下-
const input = {
"test": 1,
"test2": "2",
}
const output = {
test: {
"testData": 1,
"testIsError": false,
},
test2: {
"test2Data": "2",
"test2IsError": false,
}
}
我希望输出键的类型为object,其中${key}Data
与输入对象中的键的类型相同。
我知道这可以更好地优化到下面的输出对象,这是干净的&可读如下-
type Value<T> = {
data: T;
isError: boolean;
}
type BetterType<T> = {
[K in keyof T]: Value<T[K]>;
}
const test = {
"test": 1,
"test2": "2",
}
const test2: BetterType<typeof test> = {
test: {
data: 1,
isError: false,
},
test2: {
data: "will infer as string",
isError: false,
}
}
但是,出于好奇,我想知道我的上述原始问题是否可以在打字稿中解决。
我倾向于把你的字体定义为:
type InToOut<T extends object> = { [K in keyof T]:
{ [P in "Data" | "IsError" as `${Exclude<K, symbol>}${P}`]:
P extends "Data" ? T[K] : boolean
}
};
会导致我认为你正在寻找的转换:
const input = {
"test": 1,
"test2": "2",
}
type Out = InToOut<typeof input>;
/* type Out = {
test: {
testData: number;
testIsError: boolean;
};
test2: {
test2Data: string;
test2IsError: boolean;
};
} */
工作原理:
type InToOut<T extends object> = { [K in keyof T]:
{ [P in "Data" | "IsError" as `${Exclude<K, symbol>}${P}`]:
P extends "Data" ? T[K] : boolean
}
};
这是一个映射类型,即输入类型T
中每个具有K
键的属性都转换为具有相同键的输出属性类型。输出属性类型为
{ [P in "Data" | "IsError" as `${Exclude<K, symbol>}${P}`]:
P extends "Data" ? T[K] : boolean
}
是另一个映射类型;它迭代字符串字面值类型"Data"
和"IsError"
,并使用键重映射为其中的每个字面值输出一个带有转换后的键的属性。转换后的键是`${Exclude<K, symbol>}${P}`
,它是一个模板字面值类型,表示原始对象中的键K
与"Data"
或"IsError"
中的键P
的连接。从概念上讲,模板文字类型将是`${K}${P}`
,但由于K
可能是symbol
,并且由于symbol
不能通过模板文字正确序列化,我们需要从考虑中排除symbol
。因此使用Exclude
实用程序类型和Exclude<K, symbol>
.
所以关键是K
-加上-"Data"
-或K
-加上-"IsError"
。这个值是一个条件类型P extends "Data" ? T[K] : boolean
,所以如果"Data"
已经被添加,那么属性就是T[K]
,对应于原始T
对象类型中键K
的属性的索引访问类型;如果添加了"IsError"
,则属性为boolean
。
顺便说一下,您不必使用键重映射和条件类型来获得等效类型,但我喜欢输出类型的方式。一个等价的解是:
type InToOut<T extends object> =
{ [K in Exclude<keyof T, symbol>]:
{ [P in `${K}Data`]: T[K] } &
{ [P in `${K}IsError`]: boolean }
};
type Out = InToOut<typeof input>;
/* type Out = {
test: {
testData: number;
} & {
testIsError: boolean;
};
test2: {
test2Data: string;
} & {
test2IsError: boolean;
};
} */
使用交集将K
+"Data"
和K
+"IsError"
属性连接成一个对象。
Playground链接到代码