为什么具有模板文字键的条件对象类型会匹配任何对象,而不管其键是什么?

问题描述 投票:0回答:1

我想创建一个 Typescript 实用程序类型,如果对象包含与模板匹配的键,则它可以从复杂的对象联合中提取某些子类型。为了简单起见,假设我想匹配包含以下划线开头的任何键的对象类型。

但是,带有模板文字的键的条件类型似乎总是匹配任何对象,无论是否存在匹配的键。这是一个简化的示例:

// ❌ Conditional type on object with template literal key not working as expected
type HasTemplateProperty<T> = T extends { [key: `_${string}`]: string } ? true : false

type testA1 = HasTemplateProperty<{ _test: string }> // ✅ true, as expected
type testA2 = HasTemplateProperty<{ test: string }> // ❌ true, not expected
type testA3 = HasTemplateProperty<{}> // ❌ true, not expected
type testA4 = HasTemplateProperty<number> // ✅ false, as expected

// ✅ Conditional type on object with string literal key is working as expected
type HasLiteralProperty<T> = T extends { _test: string } ? true : false

type testB1 = HasLiteralProperty<{ _test: string }> // ✅ true, as expected
type testB2 = HasLiteralProperty<{ test: string }> // ✅ false, as expected
type testB3 = HasLiteralProperty<{}> // ✅ false, as expected
type testB4 = HasLiteralProperty<number> // ✅ false, as expected

// ✅ Conditional type template literal on its own is working as expected
type MatchesTemplate<T> = T extends `_${string}` ? true : false

type testC1 = MatchesTemplate<'_test'> // ✅ true, as expected
type testC2 = MatchesTemplate<'test'> // ✅ false, as expected
type testC3 = MatchesTemplate<'test_'> // ✅ false, as expected
type testC4 = MatchesTemplate<number> // ✅ false, as expected

Typescript Playground 上的演示

我已经看到了有关 Typescript 模板文本和对象键中意外行为的一些其他讨论,但我找不到有关此特定行为的任何信息。我还在 Typescript 的 GitHub 上发现了这个未解决的问题,其中讨论了计算属性键名称意外地扩展为

string
,这可能是相关的,但没有解释“testA3”,其中没有属性的对象被视为匹配。

为什么 Typescript 会这样做,有没有办法获得 Typescript 实用程序类型的预期行为,该类型匹配包含与

{ _anything: '...', anythingElse: 123 }
等模板匹配的键的对象,但不匹配不包含
{ anything: '...', anythingElse: 123 }
等键的对象?

(我认为映射属性在我的情况下不起作用,因为我想在对象级别而不是单个属性级别获得匹配)

typescript conditional-types typescript-template-literals
1个回答
0
投票

您在条件类型中描述的是索引访问类型。所以它可以包含键

_${string}
的属性,也可以不包含。这就是为什么
{}
给出
true
。由于
{test: string}
在结构上满足
{}
,因此也给出
true
:

type Check = { test: string } extends {} ? true : false; // true

这是因为

{ test: string }
可以用作TS中的
{}

为了使您的条件类型起作用,您可以检查实际的属性键和值是否满足您的条件: 游乐场

type HasTemplateProperty<T> = keyof T extends `_${string}` ? T[keyof T] extends string ? true : false : false;

type testA1 = HasTemplateProperty<{ _test: string }> // ✅ true, as expected
type testA2 = HasTemplateProperty<{ test: string }> // ✅ false, as expected
© www.soinside.com 2019 - 2024. All rights reserved.