打字稿遍历打字稿对象:界面但继续打字

Typescript itterate through typescript object : interface but keep typing

我有一个方法,它接受一个对象,它是一个部分我想使用 Object.entries 遍历该对象,然后基于我想执行一些操作的键。该接口的键是字符串,而值是字符串|数字|字符串[]|日期时间。我希望传递给它的函数知道从密钥传入的是什么。

我目前的密码是

public partialUpdate = async (id: number, fieldsToUpdate: Partial<IClient>) => {

    type temp = Partial<Record<keyof IClient, (val : string) => void>>;
    const mapping : temp = {
        name: (val) => client.setName(val),
        website: (val) => client.setWebsite(val),
        sic_codes: (val) => client.setSicCodes(val),
        phone_number: (val) => client.setPhoneNumber(val),
    };
    Object.entries(fieldsToUpdate).forEach(([key, value]) => mapping[key](value));
    return this.clientRepository.update(id, client);
};

界面是

export interface IClient
{
    id: number,
    creation_date: DateTime,
    name:string,
    website:string,
    phone_number:string,
    industry:string,
    sic_codes:string[],
}

我目前遇到的问题是,如果它都是字符串类型,那么没有问题,但是因为 sic_codes 是一个字符串[],它会导致类型错误。有什么方法可以让 const 映射知道键中的 val 类型?

你原来的类型

Record<keyof IClient, (val: string) => void>

使用 the Record<K, V> utility type, which is implemented as a mapped type。让我们根据映射类型明确地写出来:

{[K in keyof IClient]: (val: string) => void}

所以,这里的问题是 val 类型 string 不一定是 keyof IClient 中每个键 K 的值类型 IClient .修复方法是将 string 替换为 IClient[K]indexed access type 表示“键 KIClient 的值类型”:

type Test = { [K in keyof IClient]: (val: IClient[K]) => void };
/* type Test = {
  id: (val: number) => void;
  creation_date: (val: Date) => void;
  name: (val: string) => void;
  company_number: (val: string) => void;
  website: (val: string) => void;
  phone_number: (val: string) => void;
  industry: (val: string) => void;
  sic_codes: (val: string[]) => void;
} */

看起来不错。


如果我们将其插入到您使用原始类型的位置,仍然存在错误。 Object.entries() 的类型不够强,无法满足您在 TypeScript 中的需求。请参阅您正在处理的特定密钥类型 K 中的 for more information. You can give it stronger typings (even though this isn't 100% safe). Even if you do that, your forEach() callback needs to be generic。在那个回调中你需要确保你的 value 和你的 mapping[key] 不是 undefined,如果你想安全的话:

让我进行这些更改,只是为了证明它按您的预期工作:

// Give stronger typings to Object.entries()
type Entry<T extends object> = { [K in keyof T]-?: [K, T[K]] }[keyof T];
function entries<T extends object>(obj: T) {
    return Object.entries(obj) as Entry<T>[];
}

const partialUpdate = async (id: number, fieldsToUpdate: Partial<IClient>) => {
    const client = new Client();    
    const mapping: Partial<{ [K in keyof IClient]: (val: IClient[K]) => void }> = {
        name: (val) => client.setName(val),
        company_number: (val) => client.setCompanyNumber(val),
        industry: (val) => client.setIndustry(val),
        sic_codes: (val) => client.setSicCodes(val),
    };
    const e = entries(fieldsToUpdate).forEach(<K extends keyof IClient>(
        [key, value]: [K, IClient[K] | undefined]) => value && mapping[key]?.(value));
    return null;
};

万岁,没有错误!

Playground link to code