Oli*_*Ash 2 generics typescript
在下面的示例中,我想不出任何分配Pick<Object, Key>
给Partial<Object>
不合理的情况,因此我希望这是允许的。
谁能澄清为什么不允许?
const fn = <T, K extends keyof T>(partial: Partial<T>, picked: Pick<T, K>) => {
/*
Type 'Pick<T, K>' is not assignable to type 'Partial<T>'.
Type 'keyof T' is not assignable to type 'K'.
'keyof T' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string | number | symbol'.
*/
partial = picked;
};
Run Code Online (Sandbox Code Playgroud)
@TitianCernicovaDragomir 本质上是正确的,编译器通常无法对未解析的泛型类型进行复杂的类型分析。它在具体类型上表现得更好。请参阅Microsoft/TypeScript#28884Pick
有关此问题以及Omit
互补键集的讨论。
在这些情况下,继续的唯一方法是您亲自验证分配是否正确,然后使用类型断言,如partial = picked as Partial<T>
......
...但在这种情况下我不会这样做。这里的错误确实是一个很好的错误,尽管很难看出原因,因为您基本上只是覆盖了变量partial
并且在函数范围内没有对它执行任何操作。因此,尽管代码不健全,但它是无害的,因为它不允许在其他地方造成严重破坏。让我们通过fn()
返回修改后的partial
变量来解开它:
const fn = <T, K extends keyof T>(partial: Partial<T>, picked: Pick<T, K>) => {
partial = picked; // error, for good reason
return partial; //
};
Run Code Online (Sandbox Code Playgroud)
所以,基本问题是 是Pick<T, K>
比更广泛的类型T
。T
它包含键为 in 的属性K
,但不知道是否包含键不在中的属性K
。我的意思是,类型的值Pick<{a: string, b: number}, "a">
很可能有一个b
属性。如果它确实有一个,则它不必是 类型number
。Pick<T, K>
所以将 type 的值赋给type 的变量是错误的Partial<T>
。
让我们用一个愚蠢的例子来充实这一点。假设您有一个Tree
接口和一个类型为 的对象Tree
,如下所示:
interface Tree {
type: string;
age: number;
bark: string;
}
const tree: Tree = {
type: "Aspen",
age: 100,
bark: "smooth"
};
Run Code Online (Sandbox Code Playgroud)
并且您还有一个Dog
接口和一个类型为 的对象Dog
,如下所示:
interface Dog {
name: string;
age: number;
bark(): void;
}
const dog: Dog = {
name: "Spot",
age: 5,
bark() {
console.log("WOOF WOOF!");
}
};
Run Code Online (Sandbox Code Playgroud)
因此,dog
和tree
都有一个数字age
属性,并且它们都有bark
不同类型的属性。一个是a string
,另一个是方法。请注意,这dog
是一个完全有效的 type 值Pick<Tree, "age">
,但却是一个无效的type 值Partial<Tree>
。因此,当您致电时fn()
:
const partialTree = fn<Tree, "age">(tree, dog); // no error
Run Code Online (Sandbox Code Playgroud)
我的修改fn()
返回dog
为Partial<Tree>
,有趣的开始:
if (partialTree.bark) {
partialTree.bark.toUpperCase(); // okay at compile time
// at runtime "TypeError: partialTree.bark.toUpperCase is not a function"
}
Run Code Online (Sandbox Code Playgroud)
这种不健全性的泄漏正是因为Pick<T, K>
不知道排除或以其他方式限制“未采摘”的属性。您可以创建自己的属性,其中明确排除来自not inStrictPicked<T, K>
的属性:T
K
type StrictPicked<T, K extends keyof T> = Pick<T, K> &
Partial<Record<Exclude<keyof T, K>, never>>;
Run Code Online (Sandbox Code Playgroud)
现在你的代码更加健全(忽略像上面评论K
中那样的品牌类型之类的奇怪事情)......但编译器仍然无法验证它:
const fn2 = <T, K extends keyof T>(
partial: Partial<T>,
picked: StrictPicked<T, K>
) => {
partial = picked; // also error
partial = picked as Partial<T>; // have to do this
return partial;
};
Run Code Online (Sandbox Code Playgroud)
这仍然是这里的基本问题;编译器不能轻易处理这样的事情。也许有一天会吗?但至少它在调用方不那么容易被滥用:
fn2<Tree, "age">(tree, dog); // error, dog is not a StrictPicked<Tree, "age">
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
5530 次 |
最近记录: |