如何在TypeScript中基于另一对象属性定义对象类型?
你的需求完全可以实现!核心问题在于两个点:一是需要把ParameterDesc里的字符串类型标识(比如'number')映射成TypeScript的原始类型(number),二是要让someConfig的类型保留足够精确的信息,这样TypeScript才能正确推导每个属性对应的类型。
下面是具体的实现步骤:
1. 创建类型映射工具
首先定义一个条件类型,用来把ParameterDesc的子类型转换成对应的TS原始类型:
type TypeFromDesc<T extends ParameterDesc> = T extends NumberParameterDesc ? number : T extends StringParameterDesc ? string : T extends BooleanParameterDesc ? boolean : never;
2. 重构State类型
用上面的工具类型替换你之前直接取.type的写法,让索引类型能正确解析每个属性的类型:
type State<PDesc extends ParameterDescriptions> = { [K in keyof PDesc]: TypeFromDesc<PDesc[K]>; };
3. 让someConfig保留精确类型
注意!如果直接给someConfig标注ParameterDescriptions类型,TypeScript会把每个属性都当成ParameterDesc联合类型,丢失具体的子类型信息(比如NumberParameterDesc),这样条件类型无法正确工作。
我们可以用一个辅助函数来约束someConfig符合ParameterDescriptions结构的同时,保留精确的类型推导:
const createConfig = <T extends ParameterDescriptions>(config: T) => config; 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, }, });
4. 自动生成MyState
现在只需要基于someConfig的类型来定义MyState,它会自动继承所有属性和对应类型:
// 用类型别名直接生成 type MyState = State<typeof someConfig>; // 或者用interface继承(效果一致) interface MyState extends State<typeof someConfig> {}
此时MyState的结构完全和你手动定义的一致:
// 等价于: interface MyState { someNumProperty: number; someStrProperty: string; someBoolProperty: boolean; }
为什么之前的方法不生效?
你之前的State类型直接取PDesc[property].type,得到的是字符串字面量类型(比如'number'),而不是TypeScript的number原始类型。通过条件类型TypeFromDesc,我们把字符串标识和实际类型做了映射,解决了这个问题。另外,确保someConfig的类型足够精确是关键,否则TypeScript无法区分不同的ParameterDesc子类型。
内容的提问来源于stack exchange,提问作者epere4

