基于另一个对象的属性在对象中键入属性(在Typescript中)

时间:2018-05-31 13:36:34

标签: 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;
};

但我不知道如何建立MyStateParameterDescription实例之间的关系。

我的一个想法是让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

中找到

1 个答案:

答案 0 :(得分:1)

我们需要解决的第一个问题是,您定义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;}

游乐场link