如何查看一个Typescript类型的完整扩展合约?

122

如果我有一个看起来有点像这样的类型集合:

type ValidValues = string | number | null
type ValidTypes = "text" | "time" | "unknown"

type Decorated = {
  name?: string | null
  type?: ValidTypes
  value?: ValidValues
  title: string
  start: number
}

type Injected = {
  extras: object
}

// overriding the types from Decorated
type Text = Decorated & Injected & {
  name: string
  type: "text"
  value: string
}

我的实际代码还有更多事情要做,但这显示了核心思想。 我不想只是相信自己能够准确地理解类型之间的关系。我希望工具可以向我展示经过所有类型代数运算后Text类型定义的“计算”结果。

因此,对于上面的示例,我希望在Text中指定的字段将覆盖在Decorated类型中先前声明的内容,并且假设一个虚构的工具提示的输出将向我显示类似于:

{
  name: string
  type: "text"
  value: string
  title: string
  start: number
  extras: object
}

有没有方便的方式获取这些信息?


另请参见 https://github.com/microsoft/vscode/issues/94679 - Adam Zerner
2个回答

181

IntelliSense中显示的类型的快速信息通常不能满足要求;对于任何给定类型,您通常只会得到一个表示,这可能过于简洁甚至过于冗长。有一些建议可以使其更加灵活(例如microsoft/TypeScript#25784microsoft/TypeScript#28508),以便用户可以在其IDE中展开/折叠类型定义。但我不知道它们是否会在不久甚至远期内得到实施,因此让我们不要等待。


这是我有时用来尝试扩展类型的类型别名,以达到你所说的方式:

// expands object types one level deep
type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;

// expands object types recursively
type ExpandRecursively<T> = T extends object
  ? T extends infer O ? { [K in keyof O]: ExpandRecursively<O[K]> } : never
  : T;

这里使用条件类型推断将类型T“复制”到一个新的类型变量O中,然后是一个类似于标识符的映射类型,它遍历复制类型的属性。

条件类型推断在概念上是一个空操作,但它被用来分发联合类型并强制编译器评估条件语句的“true”分支(如果你重新定义Expand<T>而没有使用它,有时编译器会输出映射类型{[K in keyof RelevantType]: RelevantType[K]},这不是你想看到的结果)。

ExpandExpandRecursively之间的区别在于它是否应该只按原样输出属性类型(Expand),还是是否应该展开属性类型(ExpandRecursively)。在递归情况下,不要尝试深入原始类型,因此包括T extends object条件。


好的,让我们看看在您的类型上使用它会发生什么。在您的情况下,我们不需要ExpandRecursively,但是我们可以使用它...它会得到相同的结果:

type ExpandedText = Expand<Text>;

当我们在IDE中悬停在其上时(无论是TypeScript Playground还是VSCode),它会显示为:

/* type ExpandedText = {
  name: string;
  type: "text";
  value: string;
  title: string;
  start: number;
  extras: object;
 } */

如你所愿。

代码链接


2
非常感谢这个,自从我开始使用Typescript以来,我一直想要这样的东西 - 这是一个很酷的技巧。 - Guy Gascoigne-Piggford
14
如果你正在使用 VS Code,想要通过 IntelliSense 查看完整的扩展类型,你可能还需要在 tsconfig.json 中启用 noErrorTruncation(参见 https://dev59.com/LFQJ5IYBdhLWcg3wx4-p#53131824)。 - backus
7
哦,你的 Expand 应该建立在内置的实用类型 Alias<T> 或类似的东西上。 - Mirek Rusin
2
这很整洁,但它会将函数转换为空对象,因为它们扩展了对象,但没有任何属性。 - Janne Annala
2
你觉得像这个版本(https://tsplay.dev/WPgRYm)吗?如果您需要更多内容或其他内容,请发布一个新的问题帖子。 - jcalz
显示剩余5条评论

55

我在jcalz的回答的基础上补充一下与函数相关的版本。

修改后的帮助程序:

export type Expand<T> = T extends (...args: infer A) => infer R
  ? (...args: Expand<A>) => Expand<R>
  : T extends infer O
  ? { [K in keyof O]: O[K] }
  : never;

export type ExpandRecursively<T> = T extends (...args: infer A) => infer R
  ? (...args: ExpandRecursively<A>) => ExpandRecursively<R>
  : T extends object
  ? T extends infer O
    ? { [K in keyof O]: ExpandRecursively<O[K]> }
    : never
  : T;

为了测试,我将在Text类型中添加一个subobject键,并添加一个函数接口。
type Text = Decorated &
  Injected & {
    name: string;
    type: "text";
    value: string;
    subobject: Injected;
  };

interface SomeFunction {
  (...args: Text[]): Injected & { error: boolean };
}

这会得到:

enter image description here

expanded once type hover

recursive type hover

对象仍然可以正常工作:

enter image description here enter image description here


5
哇,这个很好用,也许可以编写一个扩展程序,与 IntelliSense 集成以实现这个功能..... - Gifford N.
有没有一种方法可以将展开的结构保存到剪贴板/文件中?因为当类型结构具有许多成员时,结果会被截断...或者至少有没有一种方法可以看到完整的展开而不截断? - Robert Koritnik
1
@RobertKoritnik backus的评论中提供了一个很好的解决方案,适用于jcalz答案中的评论:如果您正在使用VS Code并希望通过IntelliSense查看完整扩展类型,则可能还需要在tsconfig.json中启用noErrorTruncation(请参见stackoverflow.com/a/53131824/146044) - MHebes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接