Typescript 可选 属性 类型泛型

Typescript optional property type generic

我希望我的函数接受任何类型的对象,但如果对象有 属性 'id',请确保它是字符串或数字。

这是最简单的例子:

interface FnItem {
  id?: string | number;
};

function fn<T extends FnItem>(item: T, callback: (item: T) => void) {
  console.log(item.id);
  callback(item)
};

fn({ name: 'Michel' }, item => item.name);
fn({ name: 'Michel', id: 12 }, item => item.name);

它抛出这个错误

Argument of type '{ name: string; }' is not assignable to parameter of type 'FnItem'.
  Object literal may only specify known properties, and 'name' does not exist in type 'FnItem'
---
Property 'name' does not exist on type 'FnItem

错误告诉你所有你需要的。 name 在您的 FnItem 类型中不存在。如果您想添加任意键,可以通过添加 属性 或添加索引签名来修复它,如下所示:

interface FnItem {
  id?: string | number;
  [key: string]: any; // or whatever types you accept
};

至于泛型,我暂时不知道你需要它做什么,因为你可以简单地将函数定义为

function fn(item: FnItem): void

如果你想传递一个泛型,那么你需要告诉函数什么具体类型实现了你的接口:

interface FnItem {
  id?: string | number;
};

function fn<T extends FnItem>(item: T,  callback: (item: T) => void) {
  console.log(item.id);
  callback(item);
};

fn<myType>({ name: 'Michel', id: 12 }, (item:myType)  => { console.log(item.name); });
fn<myType>({ name: 'Michel' }, (item:myType) => { console.log(item.name); });

class myType implements FnItem
{
    name: string = "";
    id?: number;
}

Compiled example

假设 FnItem 可能是具有任何属性的任何对象或 idnumber|string 的任何对象我宁愿坚持这个解决方案:

type FnItem = Record<string, unknown>

type IdValidation<Obj extends Record<string, unknown>> =
    Obj extends { id: infer Id } ? Id extends string | number ? Obj : Obj & { id: never } : Obj;

function fn<T extends FnItem,>(item: IdValidation<T>, callback: (item: IdValidation<T>) => void) {
    console.log(item.id);
    callback(item)
};

fn({ name: 'Michel' }, item => item.name);
fn({ name: 'Michel', id: 12 }, item => item.id);

fn({ name: 'Michel' }, item => item.ya); // error
fn({ name: 'Michel', id: [] }, item => item.id); // id is highlighted as a wrong property

Playground

由于第一个参数可能是任何对象,我们应该允许传递 Record<string,unknown>,这反过来会禁用我们关于 idnumber|string 的约束。这就是我添加 IdValidation 实用程序类型的原因。它只是检查 id 属性 是否满足条件。如果满足 - 保持 id 不变,否则 - 将 id 类型替换为 never。使用 never 可让您仅突出显示不正确的 属性,这使其易于阅读和理解。

如果你对 TS 验证技术感兴趣可以查看我的文章here and here