我遇到过一些奇怪的行为.Typescript 版本:4.9.4Playground

interface TestInterface {
    A: string
}

type Test = TestInterface extends Record<infer K, any> ? K : never;
let t1: Test; // A


type Test2 = TestInterface extends Record<any, infer V> ? 1 : never;
let t2: Test2; // never


type Test3 = TestInterface extends Record<infer K, infer V> ? V : never;
let t3: Test3; // string

我有TestInterface号接口.

  • 当我试着只推断 keys 时,它起作用了.
  • 当我试图推断价值时,永远不会.
  • 当我try 推断关键字和值时,它起作用了.

为什么?

推荐答案

Record有两种行为,它总是让人们感到困惑,但界面和索引签名也让人们感到困惑.

type Record<K extends string | number | symbol, T> = { [P in K]: T; }

Kstringnumbersymbol的子类型的并集(如'A' | 'B')时,它会产生对象文字类型,如下所示:

{ A: string, B: string }

对象文字类型表示:

我知道这里列出的字段,但可以有任意数量的额外字段,并且可以是任何类型

K恰好是stringnumbersymbol的并集(如string | number)时,它会产生类型签名,如下所示

{
   [key: string]: string
   [key: number]: string
}

类型签名的基本内容是:

与这种类型的键匹配的每个可能的字段都将属于这种类型

这是非常不同的.

Record也有点奇怪,因为Record<any, unknown>的计算结果是{[k: string]: unknown},而不是Record<PropertyKey, unknown>{ [P in any]: unknown },但这并不是你感到惊讶的原因.

底线是:当您 Select 一个对象文字类型或一个索引签名时,这根本不是一种判断.


第二件令人困惑的事情是:接口不是有限的,因为declaration merging.

interface TestInterface {
    A: string
}

interface TestInterface {
    B: number
}

// same as
interface TestInterface {
    A: string
    B: number
}

如果您可以随意合并它,甚至使用module augmentation,那么很难说接口在任何给定时间都扩展了索引签名--至少这是我的心理模型,它工作得很好.

唯一的例外是,如果您显式地将接口约束到特定的索引签名:

interface TestInterface {
    [k: string]: string
    A: "foo"
}

interface TestInterface {
    B: number
//  ~~~~~~~~~ nope!
}

然后你就可以做出promise 了:如果你是extends Record<any, infer V>,V正好是string(即使A"foo")


现在,正如 comments 中所提到的,当您编写keyof TestInterface时,您将获得接口的密钥.在Record中使用它们将返回Object文字类型,这样您就很好了.

interface TestInterface {
    A: string
}
type S = TestInterface extends Record<keyof TestInterface, infer V> ? V : never; // string

只需注意,只有当接口没有显式索引签名时才会出现这种情况:如果接口有显式索引签名,则它不会返回已知键,如"A",而是类似string | number的值(当键为string时,您总是得到number),这将在反馈给Record时返回索引签名.

Typescript相关问答推荐

脉轮ui打字脚本强制执行图标按钮的咏叹调标签-如何关闭

有没有可能使用redux工具包的中间件同时监听状态的变化和操作

我们过go 不能从TypeScript中的联合中同时访问所有属性?

为什么tsx不判断名称中有"—"的属性?'

未在DIST中正确编译TypeScrip src模块导入

Angular NgModel不更新Typescript

React Typescript项目问题有Redux-Toolkit userSlice角色问题

如何在Reaction Query Builder中添加其他字段?

为什么在leetcode问题的测试用例中,即使我确实得到了比预期更好的解决方案,这段代码也失败了?

在列表的指令中使用intersectionObservable隐藏按钮

使用条件类型的类型保护

根据类型脚本中的泛型属性 Select 类型

我怎样才能正确地输入这个函数,使它在没有提供正确的参数时出错

创建依赖函数参数的类型

在打印脚本中将对象类型转换为数组

使用泛型以自引用方式静态键入记录的键

在ts中获取级联子K类型?

在类型{}上找不到带有string类型参数的索引签名 - TypeScript

如何使用 AWS CDK 扩展默认 ALB 控制器策略?

将对象数组传递给 Typescript 中的导入函数