我正在try 使一个可重复使用的includes函数如下

type Primitive = string | boolean | number;

export const includesByAttribute = (
  attr: string,
  list: { [attr]: Primitive }[],
  val: { [attr]: Primitive },
) => {
  if (list.length > 0) return list.find((obj) => obj[attr] === val[attr]);
  return false;
};

希望根据第一个参数定义第二个和第三个参数的类型.有没有办法强制执行这一点?

我试过了

export const includesByAttribute = (
  attr: string,
  list: { [attr: string]: Primitive }[],
  val: { [attr: string]: Primitive },
) => {
  if (list.length > 0) return list.find((obj) => obj[attr] === val[attr]);
  return false;
};

当调用它时,不考虑‘Value’参数,允许‘x’的属性存在于第二个和第三个参数中

includesByAttribute('value', [{ x: 1 }], { x: 2 })) // compiles fine but doesn't achieve goal

推荐答案

为了让它起作用,你需要将你的函数设为generic.这是允许多个输入类型相互依赖的唯一方法.这里有一种写它的方法:

const includesByAttribute = <
    K extends PropertyKey,
    T extends Record<K, string | number | boolean>
>(
    attr: K,
    list: T[],
    val: NoInfer<T>,
) => {
    if (list.length > 0) return list.find((obj) => obj[attr] === val[attr]);
    return false;
};

在这里,类型参数K对应于输入attr,是constrainedPropertyKey,这意味着它应该是keylike(它是string | number | symbol;如果需要,您可以将其设置为string).

并且对应于list输入的元素的类型参数T被约束为Record<K, string | number | boolean>(使用the Record utility type),这意味着它在键K处至少应该具有对应于attrstring | number | boolean属性.这并不要求Tevery属性是Primitive,只需要位于键K的那个.

您也希望valT类型,但我已经编写了NoInfer<T>,以表明您不希望编译器从val变为infer T.如果您将其保留为T,那么编译器将从listval中推断出T,并且它可能允许在val中存在不存在于list元素中的额外键.也就是说,您希望编译器从listonly推断出T,然后只根据它推断出checkval.这样,如果您将额外的键添加到val,编译器就会发出错误.

所以NoInfer<T>的计算结果是T,但"阻止"或"屏蔽"推论.在TypeScrip5.3中,没有内置的NoInfer<T>实现,而在microsoft/TypeScript#14829中有一个长期未解决的问题需要它.在对该问题的讨论中存在它的各种用户实现,这些实现适用于各种用例.在in a comment中提到了一个简单的例子:仅intersect个对象类型为空的{},以降低推理优先级:

type NoInfer<T> = T & {}

这适用于您的示例.但下一个版本的TypeScrip很可能包括microsoft/TypeScript#56794,它本身就实现了NoInfer<T>.在这一点上,您可以放弃关于它的定义,它仍然会表现出来.


好吧,让我们测试一下:

includesByAttribute('x', [{ x: 1 }], { x: 2 }); // okay
includesByAttribute('value', [{ value: 1, x: 1 }], { value: 2, x: 2 }); // okay

includesByAttribute('value', [{ x: 1 }], { x: 2 }); // error, x is not known
includesByAttribute('value', [{ value: 1, x: 1, z: 8 }], { value: 2, x: 2 }); // error, z missing

看上go 不错.编译器根据需要接受前两个调用并拒绝后两个调用.

Playground link to code

Typescript相关问答推荐

如何在不使用变量的情况下静态判断运算式的类型?

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

如何在第一次加载组件时加载ref数组

如何为父类构造函数中的修饰属性赋值?

扩展函数签名中的参数类型,而不使用泛型

contextPaneTitleText类型的参数不能分配给remoteconfig类型的关键参数'""'''

无法从Chrome清除还原的初始状态数据

是否可以从函数类型中创建简化的类型,go 掉参数中任何看起来像回调的内容,而保留其余的内容?

如何创建一家Reduxstore ?

如何在Vue中使用Enum作为传递属性的键类型?

从非文字数组(object.keys和array.map)派生联合类型

创建依赖函数参数的类型

如何将类似数组的对象(字符串::匹配结果)转换为类型脚本中的数组?

从类型脚本中元组的尾部获取第n个类型

如何为带有参数的函数类型指定类型保护?

任何导航器都未处理有效负载为";params";:{";roomId";:";...";}}的导航操作

有没有一种方法可以提升对象的泛型级别,而对象的值是泛型函数?

仅当定义了值时才按值筛选数组

为什么TS条件返回要求不明确的强制转换而不是精确的强制转换?

从 npm 切换到 pnpm 后出现Typescript 错误