代码之家  ›  专栏  ›  技术社区  ›  kaya3 Amit Bera

如何从键而不是值推断类型参数?

  •  0
  • kaya3 Amit Bera  · 技术社区  · 5 年前

    K extends string 节点名称。一个图是通过传递一个类似 {a: ['b'], b: []} 在这个最小的例子中代表两个节点 ,有一条边 B .

    class Digraph<K extends string> {
        constructor(readonly adjacencyList: Record<K, K[]>) {}
    
        getNeighbours(k: K): K[] {
            return this.adjacencyList[k];
        }
    }
    

    K 是从数组的内容而不是从对象的属性名推断出来的。这意味着 变成 'b' 'a' | 'b' ,因此Typescript会给出一个错误,因为它认为 a

    // inferred as Digraph<'b'> instead of Digraph<'a' | 'b'>
    // error: Argument of type '{ a: string[]; b: never[]; }' is not assignable to parameter of type 'Record<"b", "b"[]>'.
    let digraph = new Digraph({
        a: ['b'],
        b: [],
    });
    

    有没有办法 K 直接从属性名推断,而不是从属性值推断?

    Playground Link


    我尝试的一个解决方案是添加另一个类型参数 T extends Record<K, K[]> constructor(readonly adjacencyList: T) {} . 然后多余的属性错误消失了,但是现在 K string .

    还有,类型 Digraph<K, T> Digraph<K, Record<K, K[]>> Digraph<K, any> 为了避开这个。我正在寻找一个解决方案,不添加额外的类型参数或改变什么 K

    0 回复  |  直到 5 年前
        1
  •  2
  •   jcalz    5 年前

    所以你的问题是,有多个推理网站的候选人 K 在类型中 Record<K, K[]> 编译器的推理算法优先于错误的推理算法。您希望能够告诉编译器它不应该使用第二个 K (在属性键位置)用于此目的。它应该只注意第二个网站之后 K 检查


    microsoft/TypeScript#14829 要求这样做 非推理型参数用法 NoInfer<T> T ,但仅限于 之后

    class Digraph<K extends string> {
        constructor(readonly adjacencyList: Record<K, NoInfer<K>[]>) { }
    
        getNeighbours(k: K): K[] {
            return this.adjacencyList[k];
        }
    }
    

    一切都应该正常。


    的版本 NoInfer 存在,在microsoft/TypeScript#14829中提到了一些适用于某些用例的用户自制实现。我喜欢的那个 tend to recommend 是:

    type NoInfer<T> = [T][T extends any ? 0 : never];
    

    T extends any ? 0 : never is(目前用于TS4.2) 推迟 T 是特定类型。那么一会儿呢 最终将评估 ,编译器无法看到这个。

    希望microsoft/TypeScript#14829最终能得到一个正式的实现,这样就可以放弃其中提到的解决方法而支持它。或者至少现有的解决方案会升级为支持的功能(这个 type NoInfer<T> = T & {} about as supported as it can be ,但不幸的是,这将不适用于您的用例。)


    诺因费尔<T>

    let digraph = new Digraph({
        a: ['b'],
        b: [],
    }); // okay, Digraph<"a" | "b">
    
    let badDigraph = new Digraph({
        a: ['c'], // error, "c" is not assignable to "a" | "b"
        b: []
    })
    

    Playground link to code

        2
  •  2
  •   Oblosys    5 年前

    看来你在找一个 NoInfer<T> 类型说明 T 应仅用于类型检查,而不用于推断,如中所述 this TypeScript issue . 它还没有被添加到TypeScript中,但是 this definition from jcalz

    type NoInfer<T> = [T][T extends any ? 0 : never];
    

    如果你把唱片改写成 Record<K, NoInfer<K>[]>

    class Digraph<K extends string> {
        constructor(readonly adjacencyList: Record<K, NoInfer<K>[]>) {}
    
        getNeighbours(k: K): K[] {
            return this.adjacencyList[k];
        }
    }
    

    digraph 示例键入正确:

    // inferred type: Digraph<"a" | "b">
    let digraph = new Digraph({
        a: ['b'],
        b: [],
    });
    

    TypeScript playground

    请记住,它仍然是一种黑客虽然,和未来的改进可能使类型检查器推断 NoInfer<T> = T 阻止它阻碍推理。