在打字稿中,您只能使用string
,number
或symbol
索引对象。
我想要一个允许将泛型参数用作索引的泛型。
function foo<T extends number|string>(a: T): void {
let x: any = {};
x[a] = 42; // Error: an index expression argument must be of type...
}
如果我投射到number|string
,它会编译:
function foo<T extends number|string>(a: T): void {
let x: any = {};
let i: number|string = a;
x[i] = 42; // OK
}
因此,类型检查器非常智能,知道如果T
扩展number|string
,那么我可以将其分配给number|string
(然后我可以使用它来索引对象)但出于某种原因,它不会让我直接用T
索引。
有没有办法在泛型约束中指定传递的类型可以用作索引器?
注意:我正在使用通用而不是number|string
,因为我想限制通用只接受来自特定enum
或的值字符串文字联盟。
编辑:这是一个更具说服力的例子(更接近我真正想要做的事情),是:
// The following line fails to compile even though Key is number | string
type Dictionary<Key extends number | string, Value> = { [index: Key]: Value };
// Had it compiled I would expect the following behaviour
type Answer = "yes" | "no" | "maybe";
let grades = {} as Dictionary<Answer, number>;
grades["yes"] = 10;
grades["nope"] = 5; // Should be an error "nope" is not assignable to Answer
答案 0 :(得分:1)
// The following line fails to compile even though Key is number | string type Dictionary<Key extends number | string, Value> = { [index: Key]: Value };
使用in
关键字:
type Dictionary<TKey extends number | string, TValue> = {
[key in TKey]: TValue
};
答案 1 :(得分:0)
如果你实际上要传递扩展字符串或数字的类的实例,那么坚持使用通用方法,然后将其转换 问题是编译器无法知道传递值的类型(在运行时),所以它会警告你,通过强迫你告诉它它没关系,它应该信任你
如果您只想将参数的类型限制为特定的枚举或字符串文字,那么您可以这样做:
enum MyEnum { A, B, C };
function foo(a: MyEnum): void;
function foo(a: "A" | "B" | "C"): void;
function foo(a: any): void {
let x: any = {};
x[a] = 42;
}
好的,您可以使用toString
代替转换:
function foo<T extends number|string>(a: T): void {
let x: any = {};
x[a.toString()] = 42;
}
但如果你正在使用它,那么你几乎可以传递任何东西(只要它有toString
方法)。
这很有效,因为对象的键总是javascript中的字符串,即使在执行:
let o = { 1: "one", 2: "two", 3: "three" };
Object.keys(o).forEach(key => console.log(typeof key)); // string (x3)
否则你需要施放。
您有两种选择:
(1)演员:
class A<T extends number | string> {
private x: any;
private key: T;
constructor(key: T) {
this.x = {};
this.key = key;
}
fn(value: any) {
this.x[this.key as number | string] = value;
}
}
(2)toString:
class A<T extends number | string> {
private x: any;
private key: T;
constructor(key: T) {
this.x = {};
this.key = key;
}
fn(value: any) {
this.x[this.key.toString()] = value;
}
}
它们都是等价的,因为在将变量用作对象中的索引之前,javascript将调用toString
本身。
您可能希望编译器只是让您这样做,但它只是试图保护您。
因为键总是字符串,使用toString
然后编译器的强制执行似乎很奇怪,所有这些在javascript中工作:
let obj = {};
let date = new Date();
obj[date] = 3;
但会引发编译错误:
索引表达式参数的类型必须为&#39; string&#39;,&#39; number&#39;, &#39;符号&#39;或&#39;任何&#39;
我想这是因为作为开发人员,如果它不会出错,那么您认为密钥是Date
的一个实例,然后期望得到它在迭代键时返回(例如)。
通过引发错误,编译器会让您知道您正在做的事情并不像您预期的那样直截了当,但是通过投射您告诉它您已经意识到这一点
你想要的是目前不可能的,但它将是打字稿2.1.3 本期内容更多内容:String literal types as index signature parameter types?
但是,如果这是您的方案,那么您只需使用Map object:
type Answer = "yes" | "no" | "maybe";
let grades = new Map<Answer, number>();
grades.set("yes", 10);
grades.set("nope", 5); // error: Argument of type '"nope"' is not assignable to parameter of type 'Answer'