typescript - 从受约束的泛型参数中提取数据属性名称
问题描述
我们有一个泛型类型,它成功地从它接收的类型中提取数据属性。它在操场上,它的工作原理是这样的。
type DataPropertyNames<T> = {
[K in keyof T]: T[K] extends Function ? never : K;
}[keyof T];
type FooBarBaz = {
foo: string;
bar: Function;
baz: number;
};
const f1 = () => {
type DataProps = DataPropertyNames<FooBarBaz>;
const x: DataProps = 'foo'; // good, no error, because foo is a string
const y: DataProps = 'bar'; // good, expected error, because bar is a Function.
return {
x,
y
};
};
上面的例子展示了DataPropertyNames
当我们传递一个具体类型比如FooBarBaz
. 正如预期的那样,它丢弃了作为函数的道具,并保留了不是函数的道具。
我们的问题是为什么以下内容不能按预期工作。我们期望它DataProps
会接受'foo'
字符串,因为T
extends FooBarBaz
。
const f2 = <T extends FooBarBaz>() => {
type DataProps = DataPropertyNames<T>;
const x: DataProps = 'foo'; // bad, unexpected error
const y: DataProps = 'bar';
return {
x,
y
};
};
为什么 TypeScript 不能找出T
至少与它扩展的类型具有相同的数据属性名称?如果有的话,我们如何说服 TypeScript 呢?
解决方案
Mu-Tsan Tsai 的答案是正确的,我只是想对其进行扩展,以明确简单的泛型类型确实有效。我一直对这种情况感到好奇,我认为提供一个实验来观察类型何时变得过于复杂很有用。您可以将下面的示例(通过操场链接)与未来版本的 typescript 一起使用,以查看情况是否有所改善(我的实验在 TS 4.0.2 中运行)。
在下面的代码中,当泛型 T 直接与扩展约束一起使用时,我们可以访问泛型约束中使用的类的属性。这是好的和预期的,不足为奇。当你有一个非常简单的泛型类型(basicGenType)时,它也可以工作,在我的例子中,我使用了 keyof 运算符并且泛型类型能够从 T 中提取键。实际上我对此有点惊讶,因为我想使用 T 作为泛型参数时,任何泛型类型都会失败。对于稍微复杂的基本类型,它会失败(basicGenType2、basicGenType3)。似乎一旦我开始推断一种新类型(在本例中为 K)并使用它来组成作为断点的结果类型。一个有趣的观察是智能感知自动完成 basicGenType2 和 basicGenType3 的关键字段。
一旦我测试了扩展并推断它也失败了,这并不奇怪,它符合您的经验。
type basic = {simple:number}
type basicGenType<T> = keyof T
type basicGenType2<T> = {[K in keyof T]: K}
type basicGenType3<T> = {[K in keyof T]: T[K]}
type complexGenType1<T> = T extends basic ? number : never
type complexGenType2<T> = T extends {simple:infer R} ? R : never
const g = <T extends basic>(a:T):void => {
//directly on T typescript can use the properties of basic
a.simple = 5; //GOOD --typescript is good with this
a.simple = "5" //GOOD -- typescript complains
a.other = 5; //GOOD -- typescript complains
//for a simple generic type typescript still works
//the resulting type should only allow the value 'simple'
type basicType = basicGenType<T>
let b:basicType = 'simple'; //GOOD -- typescript is ok with this
b = 'other'; //GOOD -- typescript complains
//for a slightly more complicated simple type we fail
// the resulting type should only allow the value {simple:'simple'}
type basicType2 = basicGenType2<T>
let b2:basicType2 = {simple:'simple'}; //BAD -- typescript fails even though it should be ok with this
//for a slightly more complicated simple type we fail
// the resulting type should only allow the value {simple:number}
type basicType3 = basicGenType3<T>
let b3:basicType3 = {simple:3}; //BAD -- typescript fails even though it should be ok with this
//we've introduced extends here for complexity
//complexType shoud resolve to number here
type complexType1 = complexGenType1<T>;
let c:complexType1 = 5;//BAD -- typescript fails even though it should be ok with this
//we've introduced extends and infer here for even more complexity
//complexType2 shoud resolve to number here
type complexType2 = complexGenType2<T>;
let d:complexType2 = 5;//BAD -- typescript fails even though it should be ok with this
}
推荐阅读
- c# - 如何使用自动 if 语句将数据插入数据库?
- python - Mypy 似乎忽略了 TypeVar 类型的界限
- python-3.x - apscheduler 只调用一次函数参数
- java - Java spring - 从 ajax 发布到控制器的 415 错误
- java - 图形程序适用于 Windows 但不适用于 Mac
- angularjs - 当父范围使用带有 @ 属性的 {{}} 进行更改时,自定义指令 dom 不会更改
- php - 什么是致命错误:未捕获的错误:不在对象上下文中使用 $this?
- angular - 如何修复没有 ChangeDetectorRef 的提供者!在 Karma-Jasmine 测试中
- go - `var a chan int` 和 `a := make(chan int)` 有什么区别?
- reactjs - API 显示在控制台中,但没有显示在浏览器窗口中