Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/typescript/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在TypeScript泛型中按索引类型的值类型进行限制_Typescript_Typescript Generics - Fatal编程技术网

在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