从泛型类型创建时映射类型产生意外结果
Mapped type producing unexpected results when created from generic type
我正在使用静态对象来描述契约定义,并尝试基于它们生成类型。这些合约可以有选择地有一些选项,我想用一组选项名称来描述这些选项。然后我想使用这个数组来构造一个类型来传递选项值。所有选项都应该是必需的。见以下代码:
const definition = { optionNames: ['a', 'b'] as const }
type OptionNames = typeof definition['optionNames'][number]
// type OptionNames = "a" | "b"
type Options = Record<OptionNames, string>
// type Options = {
// a: string;
// b: string;
// }
到目前为止一切顺利,我可以毫无问题地静态构造 Options 类型。但是,当我尝试对泛型类型执行相同操作时,我得到了一些意想不到的结果:
interface Definition { optionNames?: readonly string[] }
type ConstructOptions<T1 extends Definition, T2 = T1['optionNames'][number]> = T2 extends string | number | symbol ? Record<T2, string> : {}
type GenericOptions = ConstructOptions<typeof definition>
// type GenericOptions = Record<"a", string> | Record<"b", string>
我希望 GenericOptions 与 Options 相同,但结果是 Records 的联合,每个 Records 只有 1 个选项作为必需的键。为什么会这样?
使用元组确保不单独考虑每个成分:
type ConstructOptions<T1 extends Definition, T2 = T1['optionNames'][number]> = [T2] extends [string | number | symbol] ? Record<T2, string> : {}
完整示例:
const definition = { optionNames: ['a', 'b'] as const }
interface Definition { optionNames?: readonly string[] }
type ConstructOptions<T1 extends Definition, T2 = T1['optionNames'][number]> = [T2] extends [string | number | symbol] ? Record<T2, string> : {}
type GenericOptions = ConstructOptions<typeof definition>;
一个你可能永远不会想到的奇怪的东西,但它确实有效。这里只有 TypeScript 的东西 ✌️。
顺便说一下,您可能想使用 NonNullable
或 Required
修复该错误 ;)
我正在使用静态对象来描述契约定义,并尝试基于它们生成类型。这些合约可以有选择地有一些选项,我想用一组选项名称来描述这些选项。然后我想使用这个数组来构造一个类型来传递选项值。所有选项都应该是必需的。见以下代码:
const definition = { optionNames: ['a', 'b'] as const }
type OptionNames = typeof definition['optionNames'][number]
// type OptionNames = "a" | "b"
type Options = Record<OptionNames, string>
// type Options = {
// a: string;
// b: string;
// }
到目前为止一切顺利,我可以毫无问题地静态构造 Options 类型。但是,当我尝试对泛型类型执行相同操作时,我得到了一些意想不到的结果:
interface Definition { optionNames?: readonly string[] }
type ConstructOptions<T1 extends Definition, T2 = T1['optionNames'][number]> = T2 extends string | number | symbol ? Record<T2, string> : {}
type GenericOptions = ConstructOptions<typeof definition>
// type GenericOptions = Record<"a", string> | Record<"b", string>
我希望 GenericOptions 与 Options 相同,但结果是 Records 的联合,每个 Records 只有 1 个选项作为必需的键。为什么会这样?
使用元组确保不单独考虑每个成分:
type ConstructOptions<T1 extends Definition, T2 = T1['optionNames'][number]> = [T2] extends [string | number | symbol] ? Record<T2, string> : {}
完整示例:
const definition = { optionNames: ['a', 'b'] as const }
interface Definition { optionNames?: readonly string[] }
type ConstructOptions<T1 extends Definition, T2 = T1['optionNames'][number]> = [T2] extends [string | number | symbol] ? Record<T2, string> : {}
type GenericOptions = ConstructOptions<typeof definition>;
一个你可能永远不会想到的奇怪的东西,但它确实有效。这里只有 TypeScript 的东西 ✌️。
顺便说一下,您可能想使用 NonNullable
或 Required
修复该错误 ;)