我正在try 创建一个泛型函数,它只适用于扩展为string[]的对象类型中的属性.例如,对象类型可能是:

type Thing = {
    a: Thing2[],
    b: Thing3[],
    c: number,
}
const thing2 = ['!', '@', '#'] as const;
type Thing2 = typeof thing2[number];
const thing3 = ['1', '2', '3'] as const;
type Thing3 = typeof thing3[number];

并且该功能将仅接受键ab.

删除了所有不相关的代码后,到目前为止,我有:

type KeysMatching<T, V> = {[K in keyof T]-?: T[K] extends V ? K : never}[keyof T];

function append<O extends object, K extends KeysMatching<O, string[]>>(
    obj: O,
    key: K,
    item: O[K] extends (infer U)[] ? U : never
) {
    obj[key].push(item); // Property 'push' does not exist on type 'O[K]'
}

但TypeScrip似乎没有意识到obj[key]string[]类型的.如果可能的话,我也想避免使用as这个关键字.

推荐答案

目前在TypeScrip中没有本机KeysMatching<T, V>类型运算符,其中编译器实际理解对于genericTV来说,T[KeysMatching<T, V>]是可赋值给V的.您可以实现您自己的版本,如问题代码所示,但如您所见,它在泛型函数实现中并不能像预期的那样工作.microsoft/TypeScript#48992有一个开放的特性请求来支持这类事情,但目前它还不是语言的一部分.

除非情况发生变化,否则我们将需要解决这个问题.一种解决方法是更改generic constraint的方向,以便对象类型O extends Record<K, string[]>(使用Record<K, V> utility type)代替现有约束Where K extends KeysMatching<O, string[]>,或除了现有约束Where K extends KeysMatching<O, string[]>之外.这些从不同的Angular 代表了相同或相似的东西.如果你同时做这两件事,结果会是这样:

function append<
  O extends Record<K, string[]>, 
  K extends KeysMatching<O, string[]>
>(obj: O, key: K, item: O[K][number]) {
  obj[key].push(item); // okay
}

现在编译器知道obj[key]可赋值给string[],因此接受item.我们还可以更简单地将item的类型编写为O[K][number],而不是conditional type;既然编译器知道O[K]是数组类型,我们就可以直接用number进行index into it来获得元素类型.


让我们确保它仍然可以从调用方的预期中运行:

function foo(thing: Thing) {
  append(thing, "a", "!"); // okay
  append(thing, "b", "2"); // okay
  append(thing, "c", "3"); // error!
  // ---------> ~~~ 
  // Argument of type '"c"' is not assignable to parameter of 
  // type 'KeysMatching<Thing, string[]>'.
  append(thing, "b", "!"); // error
  // --------------> ~~~
  // Argument of type '"!"' is not assignable to parameter of 
  // type '"1" | "2" | "3"'.  
}

看上go 不错.

Playground link to code

Typescript相关问答推荐

TypScript如何在 struct 上定义基元类型?

带有联合参数的静态大小数组

typescribe不能使用值来索引对象类型,该对象类型满足具有该值的另一个类型'

忽略和K的定义

类型脚本接口索引签名

我们过go 不能从TypeScript中的联合中同时访问所有属性?

类型脚本泛型最初推断然后设置

根据上一个参数值查找参数类型,也返回类型

具有动态键的泛型类型

TypeScrip表示该类型不可赋值

为什么有条件渲染会导致material 自动完成中出现奇怪的动画?

为什么我的一个合成函数不能推断类型?

React Typescript项目问题有Redux-Toolkit userSlice角色问题

如何使所有可选字段在打印脚本中都是必填字段,但不能多或少?

Typescript 类型守卫一个类泛型?

判断映射类型内的键值的条件类型

缩小对象具有某一类型的任意字段的范围

tailwind css未被应用-react

如何创建一个将嵌套类属性的点表示法生成为字符串文字的类型?

如何在没有空接口的情况下实现求和类型功能?