首页 > 解决方案 > 根据另一个对象的属性在一个对象中键入属性(在 Typescript 中)

问题描述

我真的不知道我想要的是否可能。我已经尝试了一段时间,但现在我放弃并向社区寻求帮助。

我想要一个这样的配置对象:

const someConfig: ParameterDescriptions = {
  someNumProperty: {
    name: 'numPropert',
    type: 'number',
    defaultValue: 42,
  },
  someStrProperty: {
    name: 'strPropert',
    type: 'string',
    defaultValue: 'some default value',
  },
  someBoolProperty: {
    name: 'boolProperty',
    type: 'boolean',
    defaultValue: false,
  },
};

然后是这样的对象:

interface MyState {
  someNumProperty: number;
  someStrProperty: string;
  someBoolProperty: boolean;
}

我想强制它MyState具有相同的属性someConfig,但每个ParameterDesc类型属性中提到的类型

ParameterDescriptions看起来像这样:

export interface BaseParameterDesc {
  name: string;
}

export interface NumberParameterDesc extends BaseParameterDesc {
  type: 'number';
  defaultValue?: number;
}

export interface StringParameterDesc extends BaseParameterDesc {
  type: 'string';
  defaultValue?: string;
}

export interface BooleanParameterDesc extends BaseParameterDesc {
  type: 'boolean';
  defaultValue?: boolean;
}

export type ParameterDesc =
    NumberParameterDesc|StringParameterDesc|BooleanParameterDesc;

export type ParameterDescriptions = {
  [key: string]: ParameterDesc;
};

但我不知道如何建立MyState和 的实例之间的关系ParameterDescription

我的一个想法是让 MyState 实现一些试图强制执行此操作的 State 类型,但它并没有真正起作用

type State<PDesc extends ParameterDescriptions> = {
  // The .type at the end does not work, but I don't know
  // of alternatives
  [property in keyof PDesc]: PDesc[property].type;
};

有任何想法吗?或者这是不可能的?

这篇文章中的所有代码都可以在这个Typescript Playground中找到

标签: typescript

解决方案


我们需要解决的第一个问题是您定义的方式someConfig实际上不会保留属性名称,它最终会成为一个可被任何字符串索引的变量。ParameterDescriptions如果我们使用通用辅助函数,我们可以轻松地强制执行约束并保留实际的键值:

function createConfig<T extends ParameterDescriptions>(c: T): T {
    return c;
}

const someConfig = createConfig({
    someNumProperty: {
        name: 'numPropert',
        type: 'number',
        defaultValue: 42,
    },
    someStrProperty: {
        name: 'strPropert',
        type: 'string',
        defaultValue: 'some default value',
    },
    someBoolProperty: {
        name: 'boolProperty',
        type: 'boolean',
        defaultValue: false,
    },
});

现在someConfig保留了键名,我们可以使用与您类似的方法,只是不能将type属性直接用作类型,我们需要一个额外的类型来映射类型名称和类型(我们也可以使用条件类型,但这似乎更简单在这种情况下)

type TypeNamesToTypes = {
    boolean: boolean;
    string: string;
    number: number;
}
type State<T extends ParameterDescriptions> = { [P in keyof T]: TypeNamesToTypes[T[P]['type']]}
type MyState = State<typeof someConfig> // same as = {someNumProperty: number;someStrProperty: string;someBoolProperty: boolean;}

游乐场链接


推荐阅读