【问题标题】:How can I see the full expanded contract of a Typescript type?如何查看 Typescript 类型的完整扩展合同?
【发布时间】:2020-01-01 03:27:33
【问题描述】:

如果我有一个看起来有点像这样的类型的集合,只会更冗长:

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
}

有没有什么方便的方法可以得到这些信息?

【问题讨论】:

    标签: typescript visual-studio-code algebraic-data-types


    【解决方案1】:

    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;
    

    那些使用conditional type inference 将类型T“复制”到一个新类型变量O,然后是一个类似身份的mapped type,它遍历复制的类型的属性。

    条件类型推断在概念上是无操作的,但它用于distribute union types 并强制编译器评估条件的“真”分支(如果您在没有它的情况下重新定义Expand&lt;T&gt;,有时编译器会只是输出映射类型{[K in keyof RelevantType]: RelevantType[K]},这不是你想看到的)。

    ExpandExpandRecursively 之间的区别在于它是否应该按原样输出属性类型 (Expand),或者是否应该扩展 属性类型 (@987654338 @)。在递归的情况下,不要尝试深入研究原始类型,这会有所帮助,这就是包含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;
     } */
    

    如你所愿。好的,希望有帮助;祝你好运!

    Link to code

    【讨论】:

    • 非常感谢,自从我开始使用 Typescript 以来,我就一直想要这样的东西 - 如此酷的 hack。
    • 如果您在 VS Code 中使用它并希望通过 IntelliSense 查看完整的扩展类型,您可能还希望在您的 tsconfig.json 中启用 noErrorTruncation(请参阅 stackoverflow.com/a/53131824/146044
    • 哦,伙计,您的 Expand 应该是内置实用程序类型 Alias 之类的。
    • 这真的很有帮助。谢谢@jcalz。有没有办法让它返回 Date 类型而不是 default recursive types
    • 这很简洁,但它会将函数转换为空对象,因为它们扩展对象但没有任何属性。
    【解决方案2】:

    只是补充jcalz's answer 与功能一起使用的版本。

    为了测试,我在Text类型中添加了一个subobject键,并添加了一个函数接口:

    type Text = Decorated &
      Injected & {
        name: string;
        type: "text";
        value: string;
        subobject: Injected;
      };
    
    interface SomeFunction {
      (...args: Text[]): Injected & { error: boolean };
    }
    

    修改后的助手:

    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;
    

    这给出了:

    对象仍然可以正常工作:

    【讨论】:

      猜你喜欢
      • 2019-04-06
      • 1970-01-01
      • 2018-01-03
      • 2020-08-08
      • 2019-11-02
      • 1970-01-01
      • 2021-03-25
      • 2021-05-03
      相关资源
      最近更新 更多