【问题标题】:Access to type declarations from dependencies in TypeScript从 TypeScript 中的依赖项访问类型声明
【发布时间】:2019-10-03 06:35:29
【问题描述】:

这里是 arg 的类型,Node.js 控制台命令解析器:

declare const flagSymbol: unique symbol;

declare function arg<T extends arg.Spec>(spec: T, options?: arg.Options): arg.Result<T>;

declare namespace arg {
    export function flag<T>(fn: T): T & { [flagSymbol]: true };

    export const COUNT: Handler<number> & { [flagSymbol]: true };

    export type Handler <T = any> = (value: string, name: string, previousValue?: T) => T;

    export interface Spec {
        [key: string]: string | Handler | [Handler];
    }

    export type Result<T extends Spec> = { _: string[] } & {
        [K in keyof T]?: T[K] extends Handler
            ? ReturnType<T[K]>
            : T[K] extends [Handler]
            ? Array<ReturnType<T[K][0]>>
            : never
    };

    export interface Options {
        argv?: string[];
        permissive?: boolean;
        stopAtPositional?: boolean;
    }
}

export = arg;

我需要注释解析结果:

import parseConsoleArgument from 'arg';

export function cli(rawConsoleCommandData: Array<string>): void {
  const consoleCommandArguments: /* ??? */ = parseConsoleArgument(
      {}, { argv: rawConsoleCommandData.slice(2)}
  );
}

逻辑正确的类型是arg.Result。但是,我无法访问它。下面的代码

import parseConsoleArgument from 'arg';

export function cli(rawConsoleCommandData: Array<string>): void {
  const consoleCommandArguments: arg.Result = parseConsoleArgument(
      {}, { argv: rawConsoleCommandData.slice(2)}
  );
}

TS2503: Cannot find namespace "arg"

【问题讨论】:

    标签: node.js typescript typescript-typings


    【解决方案1】:

    不确定为什么要注释变量,其类型将根据parseConsoleArgument 的结果推断。所以它已经是合适的类型了。如果您的团队需要对所有变量进行注释,您应该挑战 IMO,有很多类型很难在不复制大量代码的情况下进行注释。这是其中一种情况,让我们看看为什么。

    访问Result 不是问题,您只是不通过arg 命名空间访问它,您需要通过您使用的模块别名访问它:

    const consoleCommandArguments: parseConsoleArgument.Result<{}> = parseConsoleArgument(
        {}, { argv: rawConsoleCommandData.slice(2)}
    );
    

    这行得通,但你看到你有一个 parseConsoleArgument.Result 的类型参数,我使用了 {} 。在您开始添加要解析的参数之前,这似乎不是问题:

    export function cli(rawConsoleCommandData: Array<string>): void {
      const consoleCommandArguments: parseConsoleArgument.Result<{}> = parseConsoleArgument(
          { p: Number, p2: Boolean }, { argv: rawConsoleCommandData.slice(2)}
      );
      consoleCommandArguments.p // err
    }
    

    如果我们删除显式注释,我们不会收到错误,并且pnumber,正如人们所期望的那样。这是因为parseConsoleArgument 是一个通用函数,它从它的第一个参数中提取类型信息并使用条件类型来创建一个具有{ p: number, p2: Boolean } 的新类型

    如果我们想手动输入,我们需要写:

    export function cli(rawConsoleCommandData: Array<string>): void {
      const consoleCommandArguments : parseConsoleArgument.Result<{
        p: typeof Number,
        p2: typeof Boolean
      }> = parseConsoleArgument(
          { p: Number, p2: Boolean }, { argv: rawConsoleCommandData.slice(2)}
      );
      consoleCommandArguments.p // ok
    }
    

    这很有效,但比原来的要长得多(不必要),并且需要我们更改两个位置以添加新参数。我会让编译器推断类型,它非常擅长。任何想要查看实际类型的人只需将鼠标悬停在变量上,他们就会在工具提示中看到类型

    【讨论】:

    • 感谢您的回答和解释!您的解决方案有效,但请让我评论一些时刻。 1)“如果我们删除显式注释,我们不会得到错误”:我强制通过 TSLint 显式注释类型,因此必须注释任何变量和属性。 2)“哪个有效,但比原来的丑而且长得多”——类型别名解决了这个问题,不是吗?我想,通常情况下,我们需要知道我们对控制台输入的期望。所以应该声明适当的类型。
    • @GurebuBokofu tslint 是一个可配置的工具。有人将其配置为在缺少类型注释时引发错误。同样,也有一些规则使注释具有可推断类型的变量成为错误 (github.com/ajafff/tslint-consistent-codestyle/blob/master/docs/…)。这是您的决定,但我发现采用 3 行函数并将其转换为具有类型别名的更长函数会给 TS 带来坏名声。
    猜你喜欢
    • 2017-06-05
    • 1970-01-01
    • 2010-11-07
    • 2015-01-07
    • 2019-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多