从另一个只读数组创建一个只读数组
Creating a readonly array from another readonly array
我有一个这样的数组:
const arr = [{id: 1, color: "blue"}, {id: 4, color: "red"}] as const
我想从中创建一个类型,如下所示:
type Colors = ["blue", "red"]
我试过这样的事情:
type ArrTransform<T extends Readonly<{id: number, color: string}[]>> = {
[K in Exclude<keyof T, (symbol | string)>]: T[K]["color"]
}
但它似乎永远无法正确获取元素的顺序或输出数组类型的长度。
这可以用 Typescript 完成吗?
Mapped types on tuples are also tuples 自动。但是这种支持有很多“问题”,看起来您可能已经陷入了其中的几个问题。
Tuple-to-tuple 映射仅适用于 over a generic 类型。所以 type ArrTransform<T> = {[I in keyof T]: ...}
将正常工作,因为 in keyof T
使变换成为 同态 ,并且 T
是一个泛型类型参数。
一旦将其更改为 {[I in Exclude<keyof T, symbol | string>]: ...}
,大概是为了尝试过滤数字索引,您就使映射不再同态,事情就崩溃了。更糟糕的是,对于像 "0"
、"1"
、"2"
等 tuple types, the relevant "numeric" indices are actually string literal types,而不是 number 文字类型;所以你的过滤器会丢弃你关心的索引。
因此我们将其保留为 {[I in keyof T]: ...}
。
如果 {[I in keyof T]: ...}
将元组变成元组,而不必尝试在左侧显式地弄乱 keyof T
,那肯定意味着 I
只会被观察到迭代numeric-like 指数 T
,对吧?所以 T[I]
肯定可以分配给 { id: number, color: string }
,对吧?错了。
令人沮丧的是,编译器似乎在映射类型的主体内部认为 I
可能是 "push"
或 "pop"
或 "length"
,因此甚至虽然 T
被限制为 { id: number, color: string}
,但 T[I]
最终可能会变成各种各样的东西,其中大部分是函数类型。有关详细信息,请参阅 microsoft/TypeScript#27995。
所以我们不能只写这个:
type SadArrTransform<T extends Readonly<{ id: number, color: string }[]>> = {
[I in keyof T]: T[I]['color'] // error
}
因为编译器假定可能 T[I]
不会有已知的 "color"
属性。相反,您需要写一些意思是“if T[I]
有一个 "color"
属性,然后是那个 属性 的类型,否则... 哦,我不在乎... never
" 怎么样?看起来像这样:
type ArrTransform<T extends Readonly<{ id: number, color: string }[]>> = {
[I in keyof T]: T[I] extends { color: infer V } ? V : never
}
这个版本现在可以按照你想要的方式运行:
type X = ArrTransform<typeof arr>;
// type X = readonly ["blue", "red"]
我有一个这样的数组:
const arr = [{id: 1, color: "blue"}, {id: 4, color: "red"}] as const
我想从中创建一个类型,如下所示:
type Colors = ["blue", "red"]
我试过这样的事情:
type ArrTransform<T extends Readonly<{id: number, color: string}[]>> = {
[K in Exclude<keyof T, (symbol | string)>]: T[K]["color"]
}
但它似乎永远无法正确获取元素的顺序或输出数组类型的长度。
这可以用 Typescript 完成吗?
Mapped types on tuples are also tuples 自动。但是这种支持有很多“问题”,看起来您可能已经陷入了其中的几个问题。
Tuple-to-tuple 映射仅适用于 type ArrTransform<T> = {[I in keyof T]: ...}
将正常工作,因为 in keyof T
使变换成为 同态 ,并且 T
是一个泛型类型参数。
一旦将其更改为 {[I in Exclude<keyof T, symbol | string>]: ...}
,大概是为了尝试过滤数字索引,您就使映射不再同态,事情就崩溃了。更糟糕的是,对于像 "0"
、"1"
、"2"
等 tuple types, the relevant "numeric" indices are actually string literal types,而不是 number 文字类型;所以你的过滤器会丢弃你关心的索引。
因此我们将其保留为 {[I in keyof T]: ...}
。
如果 {[I in keyof T]: ...}
将元组变成元组,而不必尝试在左侧显式地弄乱 keyof T
,那肯定意味着 I
只会被观察到迭代numeric-like 指数 T
,对吧?所以 T[I]
肯定可以分配给 { id: number, color: string }
,对吧?错了。
令人沮丧的是,编译器似乎在映射类型的主体内部认为 I
可能是 "push"
或 "pop"
或 "length"
,因此甚至虽然 T
被限制为 { id: number, color: string}
,但 T[I]
最终可能会变成各种各样的东西,其中大部分是函数类型。有关详细信息,请参阅 microsoft/TypeScript#27995。
所以我们不能只写这个:
type SadArrTransform<T extends Readonly<{ id: number, color: string }[]>> = {
[I in keyof T]: T[I]['color'] // error
}
因为编译器假定可能 T[I]
不会有已知的 "color"
属性。相反,您需要写一些意思是“if T[I]
有一个 "color"
属性,然后是那个 属性 的类型,否则... 哦,我不在乎... never
" 怎么样?看起来像这样:
type ArrTransform<T extends Readonly<{ id: number, color: string }[]>> = {
[I in keyof T]: T[I] extends { color: infer V } ? V : never
}
这个版本现在可以按照你想要的方式运行:
type X = ArrTransform<typeof arr>;
// type X = readonly ["blue", "red"]