为何带模板字面量键的条件对象类型会匹配任意对象?
TypeScript模板字面量键的条件类型匹配问题与解决方案
我需要实现一个TypeScript工具类型,用于从复杂的对象联合类型中提取包含以下划线开头键的子类型。但发现用模板字面量键的条件类型会错误地匹配所有对象,哪怕对象里根本没有符合要求的键。以下是简化示例:
// ❌ 带模板字面量键的条件类型未按预期工作 type HasTemplateProperty<T> = T extends { [key: `_${string}`]: string } ? true : false type testA1 = HasTemplateProperty<{ _test: string }> // ✅ true,符合预期 type testA2 = HasTemplateProperty<{ test: string }> // ❌ true,不符合预期 type testA3 = HasTemplateProperty<{}> // ❌ true,不符合预期 type testA4 = HasTemplateProperty<number> // ✅ false,符合预期 // ✅ 带字符串字面量键的条件类型工作正常 type HasLiteralProperty<T> = T extends { _test: string } ? true : false type testB1 = HasLiteralProperty<{ _test: string }> // ✅ true,符合预期 type testB2 = HasLiteralProperty<{ test: string }> // ✅ false,符合预期 type testB3 = HasLiteralProperty<{}> // ✅ false,符合预期 type testB4 = HasLiteralProperty<number> // ✅ false,符合预期 // ✅ 单独的模板字面量条件类型工作正常 type MatchesTemplate<T> = T extends `_${string}` ? true : false type testC1 = MatchesTemplate<'_test'> // ✅ true,符合预期 type testC2 = MatchesTemplate<'test'> // ✅ false,符合预期 type testC3 = MatchesTemplate<'test_'> // ✅ false,符合预期 type testC4 = MatchesTemplate<number> // ✅ false,符合预期
为什么会出现这种行为?
TypeScript对{ [key: _${string}]: string }这类索引签名的处理逻辑是宽松约束:只要对象的所有属性(如果存在)都满足“键以下划线开头且值为string”,就会判定为匹配。空对象{}或者没有下划线开头键的对象,因为不存在违反约束的属性,所以会被错误地判定为匹配。而字符串字面量键的条件类型是严格匹配,要求对象必须存在指定的键,因此不会出现这个问题。
正确实现预期的工具类型
要实现“对象中至少存在一个以下划线开头的键”的判断,需要检查对象的键集合中是否有符合模板的项。可以通过Extract工具类型筛选符合条件的键,再判断结果是否非空:
type HasTemplateProperty<T> = T extends object ? Extract<keyof T, `_${string}`> extends never ? false : true : false; // 验证测试用例 type testA1 = HasTemplateProperty<{ _test: string }>; // ✅ true type testA2 = HasTemplateProperty<{ test: string }>; // ✅ false type testA3 = HasTemplateProperty<{}>; // ✅ false type testA4 = HasTemplateProperty<number>; // ✅ false // 复杂对象测试 type testComplex1 = HasTemplateProperty<{ _name: string; age: number }>; // ✅ true type testComplex2 = HasTemplateProperty<{ username: string; email: string }>; // ✅ false
这个实现的逻辑是:
- 先判断
T是否为对象类型,非对象直接返回false - 用
Extract<keyof T,_${string}>从对象的键中筛选出所有以下划线开头的键 - 如果筛选结果是
never(说明没有符合条件的键),返回false;否则返回true
内容的提问来源于stack exchange,提问作者user56reinstatemonica8
相关产品推荐
相关产品推荐

