创建一个类型,该类型定义包含特定嵌套属性的对象的键

时间:2019-03-24 10:02:56

标签: typescript

我有一个有趣的案例,从中央JSON数据存储动态定义类型将非常有用。让我解释一下。

我有一个包含品牌列表的json文件

// brands.json
{
    "Airbus": {
        "keywords": ["A320", "A380"],
        "type": ["planes"]
    },
    "Jaguar": {
        "keywords": ["fiesta"],
        "origin": "UK",
        "type": ["cars", "glasses"]
    },
    "Nissan": {
        "keywords": ["qashqai"],
        "type": ["cars"]
    }
}

然后我有我的类型定义:

import brands from "./brands.json

type BrandNames= keyof typeof brands ;

type Brands = {
    [P in BrandNames]: {
        keywords: string[];
        origin?: string;
        type: string[];
    }
};

我创建了一个CompanieNames类型,该类型会自动生成并等于"Airbus" | "Jaguar" | "Nissan"

到目前为止一切都很好...

现在,我想创建一个CarBrands类型,该类型将等于"Jaguar" | "Nissan"

可以进行type CarBrands = Exclude<CompanieNames, "Airbus">;,但这不是动态的。

因此,我需要从Brands中过滤掉所有具有嵌套type属性的键,这些属性不包含字符串"cars"。 / p>

有可能这样做吗?

1 个答案:

答案 0 :(得分:2)

如果保留字符串文字类型的类型,我们可以做您想要的事情(即,仅提取在car字段中具有types的品牌)。但是,Typescript会将types数组中的值的类型扩展为string,因此当我们实际查看json对象的类型时,此信息对我们丢失了。

很有趣,使用3.4中的as const(尚未发布)保留所有字符串文字类型,这就是解决方案的样子:

let data = {
    "Airbus": {
        "keywords": ["A320", "A380"],
        "type": ["planes"]
    },
    "Jaguar": {
        "keywords": ["fiesta"],
        "origin": "UK",
        "type": ["cars", "glasses"]
    },
    "Nissan": {
        "keywords": ["qashqai"],
        "type": ["cars"]
    }
} as const;

type Data = typeof data;
type CarBrands = {
    [P in keyof Data]: 'cars' extends Data[P]['type'][number] ? P : never
}[keyof Data]

同样,以上内容不适用于导入的json模块,因为typescript不会在字符串数组中保留文字类型,并且在编写本文时也没有办法告诉它这样做。