从字符串列表 a 中导出类型 A
Derive a type A from a list of strings a
假设我有一个常量且不可变的字符串列表a
const a = ['b', 'c', 'd']
我想创建一个如下所示的类型 A:
type A = 'b' | 'c' | 'd'
我的问题是:如何从列表 a
中自动创建类型 A
?
另一种方法也适用于我:How to create the list a
given the type A
你想做的事情是不可能的,因为数组是在运行时创建的,并且编译器无法确定对数组的更改。因此,它无法强制执行类型系统。
例如,考虑以下内容:
const array = [ "Bob", "James", "Dan" ];
let value: *A key of array*
setTimeout(() => {
value = "James";
}, 1000);
array.splice(1, 1);
在上面的场景中会发生什么?调用 value = "James
时,"James"
已从数组中删除,因此应该抛出错误!但这发生在运行时,因此编译器无法警告您。
解决方法是使用以下结构声明一个类型,但不能对其进行修改:
type MyType = "Bob" | "James" | "Dan"
let value: MyType = "Dan";
value = "Steven" // <- Compiler error!
编辑: 在他们的评论中快速总结。
是的,你可以做到这一点。唯一的障碍是如果你写
const a = ['b','c','d'];
// inferred as string[]
然后 a
将被推断为 string[]
,因此在您尝试时忘记了文字值 'a'
、'b'
和 'c'
导出 A
。您始终可以显式注释 a
的类型:
const a: ['b','c','d'] = ['b','c','d'];
type A = typeof a[number];
//type A = "b" | "c" | "d"
但现在你在重复你自己,你正在努力避免。有解决办法:
在 I recommended a helper function (tuple()
in tuple.ts) 中推断元组类型。
const a = tuple('b','c','d');
//inferred as type ["b", "c", "d"]
type A = typeof a[number];
//type A = "b" | "c" | "d"
如果你不关心它是一个元组,你可以写一个简单的数组推理辅助函数来代替:
function inferLiteralArray<T extends string>(...arr: T[]): T[] {
return arr;
}
const a = inferLiteralArray("b", "c", "d");
//inferred as type ("b" | "c" | "d")[]
type A = typeof a[number];
//type A = "b" | "c" | "d"
两种方法都行,您无需重复。希望有所帮助;祝你好运!
更新 1
@fritz_da_silva :
awesome, it works neatly!!! the a[number] syntax looks like black magic to me. could u please elaborate on why it works?
当然可以。语法应该这样解释(为了清楚起见,请参阅我添加的括号):
type A = (typeof a)[number];
typeof a
部分使用 typeof
type query operator 获取 a
的类型,(对于 tuple()
示例)是 ["b", "c", "d"]
。
那我现在用的是indexed access operator。如果你有一个类型 T
和它的 keys 之一的类型 K
(所以它应该扩展 keyof T
;你可以使用像这样的字符串文字"length"
或像 0
这样的数字文字或这些的并集),那么 T[K]
就是该键可访问的 属性 的类型。对于类似数组的类型 A
,类型 A[number]
获取该数组类型的元素的类型。在元组示例中,这证明是一个联合,因为有多个 number
类型的键具有不同的 属性 类型。 (当您使用索引访问运算符时,T[K1|K2]
应该等于 T[K1]|T[K2]
)。观察:
type TheTuple = typeof a; // ['b','c','d'];
type Zeroth = TheTuple[0]; // 'b'
type First = TheTuple[1]; // 'c'
type Second = TheTuple[2]; // 'd'
type A = TheTuple[number]; // 'b'|'c'|'d'
这有意义吗?
假设我有一个常量且不可变的字符串列表a
const a = ['b', 'c', 'd']
我想创建一个如下所示的类型 A:
type A = 'b' | 'c' | 'd'
我的问题是:如何从列表 a
中自动创建类型 A
?
另一种方法也适用于我:How to create the list a
given the type A
你想做的事情是不可能的,因为数组是在运行时创建的,并且编译器无法确定对数组的更改。因此,它无法强制执行类型系统。
例如,考虑以下内容:
const array = [ "Bob", "James", "Dan" ];
let value: *A key of array*
setTimeout(() => {
value = "James";
}, 1000);
array.splice(1, 1);
在上面的场景中会发生什么?调用 value = "James
时,"James"
已从数组中删除,因此应该抛出错误!但这发生在运行时,因此编译器无法警告您。
解决方法是使用以下结构声明一个类型,但不能对其进行修改:
type MyType = "Bob" | "James" | "Dan"
let value: MyType = "Dan";
value = "Steven" // <- Compiler error!
编辑:
是的,你可以做到这一点。唯一的障碍是如果你写
const a = ['b','c','d'];
// inferred as string[]
然后 a
将被推断为 string[]
,因此在您尝试时忘记了文字值 'a'
、'b'
和 'c'
导出 A
。您始终可以显式注释 a
的类型:
const a: ['b','c','d'] = ['b','c','d'];
type A = typeof a[number];
//type A = "b" | "c" | "d"
但现在你在重复你自己,你正在努力避免。有解决办法:
在 tuple()
in tuple.ts) 中推断元组类型。
const a = tuple('b','c','d');
//inferred as type ["b", "c", "d"]
type A = typeof a[number];
//type A = "b" | "c" | "d"
如果你不关心它是一个元组,你可以写一个简单的数组推理辅助函数来代替:
function inferLiteralArray<T extends string>(...arr: T[]): T[] {
return arr;
}
const a = inferLiteralArray("b", "c", "d");
//inferred as type ("b" | "c" | "d")[]
type A = typeof a[number];
//type A = "b" | "c" | "d"
两种方法都行,您无需重复。希望有所帮助;祝你好运!
更新 1
@fritz_da_silva
awesome, it works neatly!!! the a[number] syntax looks like black magic to me. could u please elaborate on why it works?
当然可以。语法应该这样解释(为了清楚起见,请参阅我添加的括号):
type A = (typeof a)[number];
typeof a
部分使用 typeof
type query operator 获取 a
的类型,(对于 tuple()
示例)是 ["b", "c", "d"]
。
那我现在用的是indexed access operator。如果你有一个类型 T
和它的 keys 之一的类型 K
(所以它应该扩展 keyof T
;你可以使用像这样的字符串文字"length"
或像 0
这样的数字文字或这些的并集),那么 T[K]
就是该键可访问的 属性 的类型。对于类似数组的类型 A
,类型 A[number]
获取该数组类型的元素的类型。在元组示例中,这证明是一个联合,因为有多个 number
类型的键具有不同的 属性 类型。 (当您使用索引访问运算符时,T[K1|K2]
应该等于 T[K1]|T[K2]
)。观察:
type TheTuple = typeof a; // ['b','c','d'];
type Zeroth = TheTuple[0]; // 'b'
type First = TheTuple[1]; // 'c'
type Second = TheTuple[2]; // 'd'
type A = TheTuple[number]; // 'b'|'c'|'d'
这有意义吗?