在过去的三天里,我学习TypeScript,用它撞到我的头。可以简化为:
type StrKeyStrVal = {
[key: string]: string
};
function setKeyVal<T extends StrKeyStrVal>(obj: T, key: keyof T, value: string) {
obj[key] = value;
}基本上,我有带有字符串键和(仅)字符串值的对象。我希望有一个泛型函数,它可以将这个对象上的任何字符串键设置为任何字符串值。听起来很简单,但是TypeScript (严格)抱怨上面的objkey = value;赋值。我认为这发生在以下更改之后:https://github.com/microsoft/TypeScript/pull/30769
在TypeScript 3.5之前,上述方法可以正常工作,但行为被认为是不安全的。我看不出在这种情况下这怎么会不安全。我的对象只能有字符串键,值只能是字符串,T扩展了这种类型,obj是T,键是T的键,值是字符串,这怎么可能是不安全的?我怎样才能向TypeScript展示这是好的,而又不跳出类型的安全性呢?
发布于 2020-05-29 02:39:20
出于与这不安全相同的原因,这是不安全的:调用方选择类型参数,该参数可能比泛型约束窄,使类型更窄的一种方法是缩小其属性类型。所以定义为Shirt的类型为
type Shirt = { size: "S" | "M" | "L", color: string }
const redShirt: Shirt = { size: "L", color: "red" };是StrKeyStrVal的一个子类型。
const strKeyStrVal: StrKeyStrVal = redShirt; // okay这对您来说是个问题,因为size属性严格地比string窄,如果您不小心,您将为它分配一些不可接受的string:
setKeyVal(redShirt, "size", "XXXXXL"); // oops那么,你能做什么?如果您不希望遇到上述情况,您可以只使用一个类型断言并继续前进。如果要避免类型断言,则需要重构。
与断言一样不安全的重构方法之一是删除泛型:
function setKeyVal(obj: StrKeyStrVal, key: string, value: string) {
obj[key] = value; // okay
}
setKeyVal(redShirt, "size", "XXXXXL"); // oops!这是因为TypeScript是不安全的地方,而非泛型的超级类型属性写入就是其中之一。有关此问题的讨论,请参见微软/打字稿#14150。这个问题的结论是,如果TypeScript不允许向超级类型写入属性,那么对许多人来说使用它将是令人难以置信的烦人的。所以在这里,生产力胜过类型安全。这也许就是你想要的案子。
重构的一种更安全的方法是使函数具有足够的通用性,使其只接受正确键的正确字符串:
function setKeyVal<T extends StrKeyStrVal, K extends keyof T>(obj: T, key: K, value: T[K]) {
obj[key] = value; // okay now
}实现现在被认为是安全的,上面使用redShirt的错误调用现在会在调用站点引发一个错误:
setKeyVal(redShirt, "size", "XXXXXL"); // error!
// -----------------------> ~~~~~~~~
// Argument of type '"XXXXXL"' is not assignable to parameter of type '"S" | "M" | "L"'现在我们有了一个函数,它很难不正确地使用,但也很难使用和注释。你可以得到更多的类型安全,但要付出一些代价。
我想这取决于您的用例和您想要进行的优先级。无论如何,我希望这有助于了解情况。祝好运!
https://stackoverflow.com/questions/62077475
复制相似问题