如何用TypeScript泛型限制对象键为对象数组深层值?
实现TypeScript对象键的严格限制,不存在键触发报错
我正尝试限制对象的允许键,当前代码如下,希望访问不存在于数组元素column.key中的键时,TypeScript能抛出错误:
type GroupBy = { column: { key: string } }; type GroupBys = [GroupBy, ...GroupBy[]]; type Result<TGroupBys extends GroupBys> = { [Key in TGroupBys[number]['column']['key']]: string; } const groupBy: GroupBys = [ { column: { key: 'foo', }, }, { column: { key: 'bar', }, }, ]; function getResult<TGroupBys extends GroupBys>(groupBy:TGroupBys): Result<TGroupBys> { const result = {}; for (const k in groupBy) { result[groupBy[k].column.key] = 'Some string'; } return result; } const result = getResult(groupBy); // 此操作应合法: console.log(result['foo']); // "Some string" console.log(result['bar']); // "Some string" // 此操作应触发TypeScript错误,因为'blah'不是有效键: console.log(result['blah']);
问题根源
当前代码中GroupBy的column.key被定义为宽泛的string类型,导致Result<TGroupBys>被推断为{ [key: string]: string }——任意字符串键都被允许,所以访问'blah'不会触发错误。要实现需求,必须让TypeScript知道具体的合法键字面量值。
修改方案
通过保留column.key的字面量类型,让TypeScript能精确识别合法键:
type GroupBy = { column: { key: string; }; }; // 移除groupBy的GroupBys类型标注,添加as const保留字面量类型 const groupBy = [ { column: { key: 'foo', }, }, { column: { key: 'bar', }, }, ] as const; // 调整泛型,让返回类型严格映射数组中的key字面量 function getResult<T extends readonly GroupBy[]>(groupBy: T): { [Key in T[number]['column']['key']]: string; } { // 对result做类型断言,匹配返回类型 const result = {} as { [Key in T[number]['column']['key']]: string }; for (const item of groupBy) { result[item.column.key as T[number]['column']['key']] = 'Some string'; } return result; } const result = getResult(groupBy); // 合法操作,无错误 console.log(result['foo']); console.log(result['bar']); // 触发TypeScript错误:类型“{ foo: string; bar: string; }”上不存在属性“blah” console.log(result['blah']);
核心要点
as const的作用:将数组和内部对象转为只读的字面量类型,确保key的具体值('foo'、'bar')被TypeScript保留,而非被宽泛推断为string。- 泛型与映射类型:通过
T extends readonly GroupBy[]捕获传入数组的具体结构,再用[Key in T[number]['column']['key']]将所有合法键映射为对象的属性,实现严格的键限制。 - 类型断言:函数内部对
result做类型断言,解决TypeScript无法自动推断循环赋值后对象类型的问题。
内容的提问来源于stack exchange,提问作者Hendrik Jan
相关产品推荐
相关产品推荐

