TypeScrip允许在函数类型参数上使用const修饰符,因此推断的类型与接收到的文本具有as const相同

function identity<const T>(a: T){ 
  return a
}

使用identity({ a: 42 })时,推断的类型是{ a: 42 },而不是预期的{ a: number }.这种奇怪的行为发生在以下几个方面:

function optionalIfFoo<
   K extends string, 
   const T
>(foo: K, ...rest: K extends 'foo' ? []: [bar: T]){
   return rest[0]
}

除了T的推论外,工作情况与预期一致.optionalIfFoo('notFoo', { a: 42 })中T的推断类型是{ a: number },而不是{ a: 42 }

在第二种情况下,有没有办法使其推断为{ a: 42 }

推荐答案

我注意到,const type parameters并不总是与我通常用来指导类型推断的其他"诀窍"一起工作.例如,参见microsoft/TypeScript#53813.

我不能假装知道它是怎么运作的.我的一般方法是使事情尽可能简单,以便编译器根据我们的意图进行推断.在这里给出您的代码示例,我可能会将其重构为如下版本:

function optionalIfFoo<
    K extends string,
    const U extends (K extends "foo" ? readonly [] : readonly [unknown])
>(foo: K, ...rest: U): U[0] {
    return rest[0]
}

在这里,我让U扮演你的[T]的角色,或多或少,所以要得到T,我们就写成U[0].我已经将conditional typerest参数类型移动到Uconstraint参数类型.它使用readonly tuples,这样const类型参数就可以工作了(这是一个重要的警告;可变的约束往往会做一些奇怪的事情).

无论如何,现在rest可以是U类型,这是一个裸类型参数,这通常是推断类型参数的最佳方式,因为它代表了编译器的最小工作量.换句话说,从类型K extends 'foo' ? []: [bar: T]的值推断T可能也可能不可能,但从类型U的值推断U确实应该是直截了当的.

让我们看看它是否奏效:

const v = optionalIfFoo('notFoo', { a: 42 });
// K: "notFoo", U: readonly [{ readonly a: 42; }]
// const v: { readonly a: 42; } 

const w = optionalIfFoo('foo'); 
// K: "foo", U: readonly []
// const w: undefined 

看上go 不错.编译器以预期的const方式推断U,因此返回类型也是预期的.

Playground link to code

Typescript相关问答推荐

类型脚本如何将嵌套的通用类型展开为父通用类型

如果存在ts—mock—imports,我们是否需要typescpt中的IoC容器

为什么typescript不能推断类的方法返回类型为泛型''

无法将select选项的值设置为ngFor循环中的第一个元素

如何根据另一个属性的布尔值来缩小函数属性的返回类型?

如何在Typescript 中输入两个相互关联的变量?

访问继承接口的属性时在html中出错.角形

Angular 15使用react 式表单在数组内部创建动态表单数组

如何将定义模型(在Vue 3.4中)用于输入以外的其他用途

类型脚本可以推断哪个类型作为参数传递到联合类型中吗?

TypeScrip泛型类未提供预期的类型错误

在保留类型的同时进行深层键名转换

创建Angular 为17的动态形状时出错

使用Type脚本更新对象属性

如何正确地将元组表格输入到对象数组实用函数(如ZIP)中,避免在TypeScrip 5.2.2中合并所有值

字幕子集一个元组的多个元素

基于闭包类型缩小泛型类型脚本函数的范围

从泛型对象数组构造映射类型

如何将元素推入对象中的类型化数组

如何在TypeScript中声明逆变函数成员?