从通用类型中提取模板参数

huangapple go评论56阅读模式
英文:

Extract template parameter from Generic type

问题

我知道如何从string[]中提取string,但这让我头疼:

type bla<T = number> = 123;

// 给定只有 "bla",在这种情况下如何提取模板参数 "number"?
type T = bla extends bla<infer U> ? U : never;

我认为在4年前这是不可能的,但也许有些变化了。

英文:

I know how to extract string from string[] but this is giving me a headache:

type bla&lt;T = number&gt; = 123;

// given just &quot;bla&quot;, how can I extract the template parameter &quot;number&quot; in this case?

type T = bla extends bla&lt;infer U&gt; ? U : never;

I think this wasn't possible 4 years ago but maybe something has changed.

答案1

得分: 2

最大的问题在于你的 通用 类型参数未被使用:

type Foo<T = number> = 123;
type Oops = Foo extends Foo<infer U> ? U : never;
// type Oops = unknown

这里 Foo<T> 对于任何 T 都将是 123。 TypeScript 的类型系统应该是 结构化的,而不是 名义化的。所以如果没有对 T 的结构依赖,从 Foo<T> 推断出 T 将是不可靠的,即使有时候它能够工作。这就是为什么 TypeScript FAQ 不鼓励不使用它们的类型参数的通用类型。


接下来的最大问题是 TypeScript 完全不使用 默认类型参数 进行推断,正如 microsoft/TypeScript#42064 中所述。

所以即使 TypeScript 类型被名义化对待,你可能也得不到你所期望的行为。将此与 约束 进行比较,它至少在推断中有所参与:

type Bar<T extends number = number> = 123;
type Hmm = Bar extends Bar<infer U> ? U : never;
// type Hmm = number

对于 Foo<T> 同样的论点会暗示 Bar<T> 也会失败,但这里你会得到 number 而不是 unknown。这是因为在无法推断时编译器会退而求其次地使用约束。因此,尽管 Bar<T> 对于所有的 T 都是 123,但在 T 上仍然存在一个 number 约束可以使用。


将这两者结合在一起,这意味着你所做的是不可能的。你需要重构以使用类型参数,然后确保明确提及没有类型参数的类型,以便让编译器替换默认值,只有这样才有可能正确地推断类型。

代码的 Playground 链接

英文:

The biggest problem here is that your generic type parameter is unused:

type Foo&lt;T = number&gt; = 123;
type Oops = Foo extends Foo&lt;infer U&gt; ? U : never;
// type Oops = unknown

Here Foo&lt;T&gt; is going to be 123 for any T whatsoever. TypeScript's type system is supposed to be structural and not nominal. So without a structural dependence on T, inference of T from Foo&lt;T&gt; will not be reliable, even if you can get it to work sometimes. That's why the TypeScript FAQ discourages any generic types that don't use their type parameters.


The next biggest problem is that TypeScript just does not use default type arguments for inference at all, as mentioned in microsoft/TypeScript#42064 .

So even if TypeScript types were treated nominally, you might not get the behavior you are looking for. Compare this to constraints, which at least participate somewhat in inference:

type Bar&lt;T extends number = number&gt; = 123;
type Hmm = Bar extends Bar&lt;infer U&gt; ? U : never;
// type Hmm = number

The same argument for Foo&lt;T&gt; would imply that Bar&lt;T&gt; would also fail to work, but you get number instead of unknown here. And that's because the compiler will fall back to the constraint when failing to infer. So even though Bar&lt;T&gt; is 123 for all T, there's still a number constraint on T that can be used.


Put both of those together and it means that what you're doing isn't possible. You'd need to refactor to use the type parameter and then be sure to explicitly mention the type without a type argument to get the compiler to substitute in the default, and only then would it be likely that you could infer the type properly.

Playground link to code

huangapple
  • 本文由 发表于 2023年3月31日 21:24:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/75899051.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定