我正试图编写通用的变量函数,但遇到了第一个障碍,这里有一个简单的例子失败了,我不明白为什么.

type TestType = {
    x: string,
    y: number,
}

type PickKeys<T extends object, K extends keyof T = keyof T> = T[K] extends string ? [K] : [never];

function testFn<T extends object>(...keys: PickKeys<T>) {}

testFn<TestType>("x"); // Argument of type 'string' is not assignable to parameter of type 'never'.

如果我从TestType签名中删除"y"或将其更改为string,它会工作,但我认为这种格式会提取其值为string类型的键.我显然错过了一些基本的东西.任何帮助都将不胜感激.

推荐答案

这里的问题是T[K] extends string ? [K] : [never]K个成员中不超过union个.它不等于(T["x"] extends string ? ["x"] : [never]) | (T["y"] extends string ? ["y"] : [never]).

只有在distributive conditional type的情况下,才会出现这种自动拆分和合并联合的情况,其中选中的类型是要在其联合成员上分发的类型参数.但T[K]不是一个类型参数(T是一个类型参数,K是一个类型参数,但T[K]不是…很像t可能是一个变量,k可能是一个变量,但t[k]可能不是),所以T[K] extends ... ? ... : ...根本不会分布在联合体上.在任何情况下,你都希望在K而不是T[K]年的unions 中进行分配.

所以你的PickKeys等于

type PickKeys1<T extends object> = 
  T[keyof T] extends string ? [keyof T] : [never];

如果你插上TestType,你会得到

type PickKeysTestType = 
  TestType[keyof TestType] extends string ? [keyof TestType] : [never];
type PickKeysTestType1 = 
  (string | number) extends string ? ["x" | "y"] : [never];
type PickKeysTestType2 = 
  [never];

因为string | number不是string的子类型,所以条件类型的计算结果是false分支,它只是never.哎呀.


如果你想在K个联合体中进行分配,你可以用一个"无操作"的分配条件类型来包装整个过程:

type PickKeys2<T extends object, K extends keyof T = keyof T> = 
  K extends unknown ? T[K] extends string ? [K] : [never] : never;

然后事情按预期进行:

function testFn<T extends object>(...keys: PickKeys2<T>) { }
testFn<TestType>("x"); // okay

other ways个实现类似PickKeys的东西(我不确定为什么我们需要元组),但这超出了问题的范围.

Playground link to code

Typescript相关问答推荐

是否可以根据嵌套属性来更改接口中属性的类型?

泛型和数组:为什么我最终使用了`泛型T []`而不是`泛型T []`?<><>

webpack错误:模块找不到.当使用我的其他库时,

泛型函数中输入参数类型的推断问题

如何提取密钥及其对应的属性类型,以供在新类型中使用?

使用2个泛型类型参数透明地处理函数中的联合类型

布局组件中的Angular 17命名路由出口

如何将对象数组中的键映射到另一种对象类型的键?

如何创建由空格连接的多个字符串的类型

接受字符串或数字的排序函数

对有效枚举值的常量字符串进行常规判断

如何在打字角react 式中补齐表格组

必需的输入()参数仍然发出&没有初始值设定项&q;错误

推断集合访问器类型

如何在Reaction Native中关注屏幕时正确更新状态变量?

如何从抽象类的静态方法创建子类的实例?

打字:注解`是字符串`而不是`布尔`?

将带有额外id属性的Rust struct 展平回TypeScript单个对象,以返回wasm-bindgen函数

typeof 运算符和泛型的奇怪行为

使用useEffect和useCallback是多余的吗?