【问题标题】:Get completions for complex types获取复杂类型的补全
【发布时间】:2019-12-24 11:37:43
【问题描述】:

我想将myG 传递给Foo 并获得Foo.getOBJ/KEY_OF_OBJ 构造函数arg 的完成(在vscode 中),我可以让它工作以便我得到Foo.getOBJ/KEY_OF_OBJ 的完成,但不是同时完成。

interface G {
  [key: string]: string;
}
interface GE<T extends G> {
  [key: string]: T;
}

class Foo<T extends G, K extends GE<T>> {
  constructor(OBJ: K, KEY_OF_OBJ: keyof K) {}
  get(keyOfG: keyof T) {}
}

interface myG extends G {
  d: string;
  e: string;
}

const foo = new Foo<myG>(
  {
    a: {
      d: '',
      e: ''
    },
    c: {
      d: '',
      e: ''
    }
  },
  'c'
);
foo.get('');

编辑: 必须像这样声明Foo new Foo&lt;myG, {a: myG, c: myG}&gt; 也是可以接受的,我这样声明它的唯一问题是,Foo.get 没有自动完成,这是因为 ts 认为它​​是一个字符串,如果我删除 @ 987654335@,它可以工作,但我希望 Tkey: stringValue 对,我想我的问题已经解决了,除非有办法将 T 限制为只有字符串值。

【问题讨论】:

  • 这里发生了很多事情,每一个都可能是它自己的问题。首先:您似乎想指定T 泛型类型参数,同时让编译器在您调用new Foo&lt;myG&gt;(....) 时推断K 泛型类型参数。但是 TypeScript 不支持这种类型参数的部分推断,所以you'd need to use a workaround for it。续:
  • 另一个问题是你有一个string | "d" | "e" 类型的值并且你希望编译器自动完成"d""e"...但当然string | "d" | "e" 只是string并且编译器在建议完成时忘记了所有关于"d""e" 的信息。这是an open issue,也有解决方法。您在问题中问的是两个问题中的哪一个?

标签: typescript


【解决方案1】:

我假设您可以通过手动指定 TK 泛型类型参数来构造 Foo 实例:

const foo = new Foo<myG, { a: myG, c: myG }>(
    {
        a: { d: "", e: "" },
        c: { d: "", e: "" }
    }, "c"
);

所以你的问题是:当string 索引签名吸收"d""e" 时,我们如何在我们的IDE 中自动完成myG 的已知文字键"d""e" ?也就是说,由于"d" | "e" | string 的计算结果仅为string,我们如何让IDE“记住”"d""e"

关于这个有一个open issue (Microsoft/TypeScript#33471),所以还没有简单而明显的答案。有哪些复杂和/或不明显的答案?这是一个。

首先,使用existing solution 从具有索引签名的类型中获取已知的文字键:

type KnownKeys<T> = {
  [K in keyof T]: string extends K ? never : number extends K ? never : K
} extends { [_ in keyof T]: infer U } ? U : never;

让我们用myG 验证它是否正确:

type KnownKeysOfMyG = KnownKeys<myG>
// type KnownKeysOfMyG = "d" | "e"

看起来不错。现在,我们可以将Fooget()签名改为接受KnownKeys&lt;T&gt;

get(keyOfG: Extract<KnownKeys<T>, string>): void;

如果我们仍然想在get() 中接受任意的string 值,这里的解决方法是为接受get()overload signature 添加第二个string

  get(keyOfG: Extract<KnownKeys<T>, string>): void;
  get(keyOfG: string): void;
  get(keyofG: string) {
     // impl
  }

它仍然接受所有 string 值:

foo.get(""); // okay
foo.get("d"); // okay
foo.get("e"); // okay
foo.get("fkjksjfksjfkds"); // okay

但自动完成提示现在有效:

好的,希望对您有所帮助;祝你好运!

Playground link to code

【讨论】:

    【解决方案2】:

    我认为 typescript 无法在编译时知道这一点。

    您基本上是说G 可以具有任何string 的属性,因此myG 扩展了它并具有两个必需的属性de,但仍然可以添加任何新属性,使用任何名称,类型为string。所以在编译时,Typescript 无法知道来自 string 类型的所有分配属性。

    获得完成的唯一方法是在G 接口上定义一小组字符串。

    interface G {
     a ?: string;
     b ?: string;
     c ?: string;
     d ?: string;
     e ?: string;
    }
    

    这样get 只能期待来自"a" | "b" | "c" | "d" | "e" 的参数

    【讨论】:

    • 感谢您的回答,我没有将其标记为答案,因为我觉得必须有办法做到这一点,我只是不知道该怎么做。
    猜你喜欢
    • 1970-01-01
    • 2013-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-22
    • 1970-01-01
    • 2015-05-21
    相关资源
    最近更新 更多