在TypeScript泛型中按索引类型的值类型进行限制
我有一门课:在TypeScript泛型中按索引类型的值类型进行限制,typescript,typescript-generics,Typescript,Typescript Generics,我有一门课: 类adderefect{ 关键词:K; 值:T[K]&编号; 构造函数(键:K,值:T[K]&编号){ this.key=key; 这个值=加数; } 应用(状态:T):T{ //做算术运算并返回一份副本 返回Object.assign({},state{ [this.key]:this.value+状态[this.key] }); } } 其思想是,T是一个通用对象,K是该对象上的一个键。但是,作为apply方法的一部分,我希望能够对该键执行算术运算。所以,我需要以这样一种方式
类adderefect{
关键词:K;
值:T[K]&编号;
构造函数(键:K,值:T[K]&编号){
this.key=key;
这个值=加数;
}
应用(状态:T):T{
//做算术运算并返回一份副本
返回Object.assign({},state{
[this.key]:this.value+状态[this.key]
});
}
}
其思想是,T
是一个通用对象,K
是该对象上的一个键。但是,作为apply
方法的一部分,我希望能够对该键执行算术运算。所以,我需要以这样一种方式限制K,它的关联值类型是number
用法可能如下所示:
接口点={
x:数字;
y:数字;
};
常量obj:Point={x:1,y:2};
常量加法器=新的加法器效果('x',3);
加法器。应用(obj);//返回{x:4,y:2}
鉴于
接口动物={
名称:string
}
const adder=new adderefect({name:'Fido'},1);
应该失败
这感觉真的很笨拙(尤其是
('x',3)
业务…),所以我很好奇是否有一种好的方法来限制K
,从而T[K]扩展数字
可以对K
进行约束,从而强制执行T[K]为给定的T
扩展数字:我通常称之为KeysMatching
,您可以编写K扩展KeysMatching
。有关实现,请参阅
不过,我不想把它作为答案,因为我认为它对你没有多大帮助。部分原因是编译器不理解T[KeysMatching]
将与number
兼容(这将需要比当前支持的更高阶的泛型类型推理),并且最终需要大量类型断言才能继续。但更重要的是:在调用apply()
方法之前,您的adderefect
不会接受类型为t
的值,这让我相信adderefect
应该只在K
中是泛型的,而不是t
所以,我倾向于改变你的限制。。。设K
为任意属性键,并将T
约束为T[K]
处带有编号的内容。这更容易表达:T扩展记录
,编译器也更容易推理
由于在您调用apply()
之前,adderefect
的实例并不真正关心t
,因此我将t
泛型从adderefect
移到apply()
方法上。大概是这样的:
class AdderEffect<K extends PropertyKey> {
key: K;
value: number;
constructor(key: K, value: number) {
this.key = key;
this.value = value;
}
apply<T extends Record<K, number>>(state: T): Omit<T, K> & { [P in K]: number } {
return Object.assign({}, state, {
[this.key]: this.value + state[this.key]
});
}
}
K
类型推断为上面的“x”
。现在,您的点应该可以工作了:
interface Point {
x: number;
y: number;
};
const obj: Point = { x: 1, y: 2 };
const newPoint: Point = adder.apply(obj); // okay
如果x
属性存在但不是数字,则不会:
interface SomethingElse {
x: string;
y: number;
}
const els: SomethingElse = { x: "one", y: 2 };
adder.apply(els); // error!
// -------> ~~~
// string is not assignable to number
最后,让我们看看在x
属性比number
窄的情况下会发生什么:
interface BinaryPoint {
x: 0 | 1;
y: 0 | 1;
}
const bin: Point = { x: 1, y: 1 };
const somethingNew = adder.apply(bin);
// const somethingNew: Pick<Point, "y"> & { x: number; }
// equivalent to const somethingNew: { x: number, y: 0 | 1 }
const newBinaryPoint: BinaryPoint = adder.apply(bin); // error!
// -> ~~~~~~~~~~~~~~
// number is not assignable to 0 | 1
接口二进制点{
x:0 | 1;
y:0 | 1;
}
常数bin:Point={x:1,y:1};
const somethingNew=加法器应用(bin);
//const somethingNew:Pick&{x:number;}
//等价于const somethingNew:{x:number,y:0 | 1}
常量newBinaryPoint:BinaryPoint=adder.apply(bin);//错误!
// -> ~~~~~~~~~~~~~~
//数字不能分配给0 | 1
您可以将加法器
应用于二进制点
,但结果不再是二进制点
,而是类似{x:number,y:0 | 1}
的值。因此,它允许您调用它,但不允许您将结果分配给二进制点
好的,希望这有帮助;祝你好运
哇,太有帮助了!非常感谢你!这很有效。发布后,我意识到一个遗漏改变了一些实现。下面是我真正的类定义:类adderefect实现了Effect
因此,我最终需要T来实现“Effect”。
interface BinaryPoint {
x: 0 | 1;
y: 0 | 1;
}
const bin: Point = { x: 1, y: 1 };
const somethingNew = adder.apply(bin);
// const somethingNew: Pick<Point, "y"> & { x: number; }
// equivalent to const somethingNew: { x: number, y: 0 | 1 }
const newBinaryPoint: BinaryPoint = adder.apply(bin); // error!
// -> ~~~~~~~~~~~~~~
// number is not assignable to 0 | 1