我想要实现的是,仅当设置了可以具有特定值的特定泛型时,我的界面中的某些键才有效.

像这样的东西

type DeviceType = "A" | "B" | "default"

interface IIconSet<DEVICE extends DeviceType> {
    info: string;
    download: DEVICE extends "A" ? string : never;
    refresh: DEVICE extends ("A" | "B") ? string : never;
}

然而,如果我像这样使用它,显然不能正确地推断界面中应该出现什么键,因为使用了以下对象

export const icons: IIconSet<"default"> {
    info: "svgString"
}

我得到的错误是:Type is missing the following properties from type IIconSet<"default">: download, refresh.在指定IIconSet<"B">时也会发生同样的情况(在本例中,只要添加refresh键,我就会得到缺少download的错误).

然而,当使用接口IIconSet<"A">定义对象时,它工作得很好.

我试图将上述字段设置为可选,但后来,当我想要将图标集从工厂中删除时,例如将类型转换为IIconSet<"A">,我得到了一个错误,download可能未定义.

当传递的泛型不符合约束时,正确的解决方案是什么--排除键?

推荐答案

你需要的是discriminated unions美元.我们将创建一个单独的类型来保存每DeviceType个:

type DeviceType = 'A' | 'B' | 'default';

现在,让我们创建一个类型,该类型将每DeviceType个保存唯一的部分:

type DeviceTypeMap = {
  A: {
    download: string;
    refresh: string;
  };
  B: {
    refresh: string;
  };
};

我们现在唯一需要的就是你改装IIconSet.为了使这种类型更灵活,我将泛型参数设置为可选,并添加了conditional type distribution(稍后将显示结果).

分发后,我们得到公共部分,即info: string,如果设备类型在DeviceTypeMap中,我们从它中获得部分,并将其与公共部分相交:

type IIconSet<T extends DeviceType = DeviceType> = T extends T
  ? { info: string } & (T extends keyof DeviceTypeMap ? DeviceTypeMap[T] : {})
  : never;

测试:

// type Result = {
//     info: string;
// } | ({
//     info: string;
// } & {
//     download: string;
//     refresh: string;
// }) | ({
//     info: string;
// } & {
//     refresh: string;
// })
type Result = IIconSet

我们得到了想要的形状,因为我们使用了conditional distribution case 2:

// type Result = {
//     info: string;
// } & {
//     download: string;
//     refresh: string;
// }
type Result = IIconSet<'A'>

用途:

export const icons: IIconSet<'default'> = { // no error
  info: 'svgString',
};

export const iconsA: IIconSet<'A'> = { // no error
  info: 'info',
  download: '',
  refresh: 'a',
};

playground

Typescript相关问答推荐

如何根据数据类型动态注入组件?

如何为ViewContainerRef加载的Angular组件实现CanDeactivate保护?

返回同时具有固定键和变量键的对象的函数的返回类型

TypeScrip-将<;A、B、C和>之一转换为联合A|B|C

如何在编译时为不带常量的参数引发错误

如何将CSV/TXT文件导入到服务中?

角效用函数的类型推断

Select 下拉菜单的占位符不起作用

为什么我的查询钩子返回的结果与浏览器的网络选项卡中看到的结果不同?

map 未显示控制台未显示任何错误@reaction-google-map/api

如何将通用接口的键正确设置为接口的键

VUE 3类型';字符串';不可分配给类型';参考<;字符串&>

基于属性值的条件类型

Select 类型的子项

Typescript不允许在数组中使用联合类型

可以将JS文件放在tsconfig';s includes/files属性?或者,我如何让tsc判断TS项目中的JS文件?

导航链接不触发自定义挂钩

如何在TypeScript类成员函数中使用筛选后的keyof this?

定义一个只允许来自另一个类型的特定字符串文字的类型的最佳方式

解析错误:DeprecationError: 'originalKeywordKind' 自 v5.0.0 起已被弃用,无法再使用