强制执行函数的参数必须属于泛型类型T

时间:2018-03-18 17:31:39

标签: javascript typescript generics

在我的打字稿函数中,我想强制执行第二个参数的所有键必须属于第一个参数的对象。

与此类似的东西

  

mapTo([new KeyValue(' a',' b'),新的KeyValue(' x',' y')] ,{key:{key2:1}});

这个第二个参数的所有键( key2 )必须属于KeyValue对象的键

这里KeyValue是:

class KeyValue {
  key: string;
  key2: string;

  constructor(k, k2) {
    this.key = k;
    this.key2 = k2;
  }
}

喜欢

  

mapTo([new KeyValue(' a',' b')],{key:{key2:1}}); - >好的

     

mapTo([new KeyValue(' a',' b')],{key:{key3:1}}); - >错误//无效的key3

为此我实现了mapTo函数

public nest<T, K extends keyof T, K2 extends keyof T>(arr: [T], basis: {[k in K]: {[k2 in K2]}}) {
    console.log('Hello!!!');
    console.log(basis);
  }

这段代码完美无缺,但如果我在KeyValue类中添加另一个键并在参数中传递此键,就像这样:

  

mapTo([new KeyValue(&#39; a&#39;,&#39; b&#39;&#39; c&#39;)],{key:{key2:{key3:1}} });

和KeyValue:

class KeyValue {
  key: string;
  key2: string;
  key3: string;

  constructor(k, k2, k3) {
    this.key = k;
    this.key2 = k2; 
    this.key2 = k3;
  }
}

然后我上面实现的函数不会验证第三个键

所以我怎么能实现上面的功能,以便它接受动态嵌套值并完美地工作。

更多例子:

  

mapTo([new KeyValue(&#39; a&#39;,&#39; b&#39;&#39; c&#39;)],{key:1}); - &GT;好的

     

mapTo([new KeyValue(&#39; a&#39;,&#39; b&#39;&#39; c&#39;)],{key:{key1:1}}); - &GT;好的

     

mapTo([new KeyValue(&#39; a&#39;,&#39; b&#39;&#39; c&#39;)],{key1:{key:1}}); - &GT;好的

     

mapTo([new KeyValue(&#39; a&#39;,&#39; b&#39;&#39; c&#39;)],{key1:{key:{key3:1}} }); - &GT;好的

     

mapTo([new KeyValue(&#39; a&#39;,&#39; b&#39;&#39; c&#39;)],{key1:{key:{key4:1}} }); - &GT;错误// key4不存在

     

mapTo([new KeyValue(&#39; a&#39;,&#39; b&#39;&#39; c&#39;)],{key3:{key2:0}); - &GT;好

1 个答案:

答案 0 :(得分:2)

您可以使用递归映射类型来定义允许在每个级别指定T的任何键的结构。我们还可以通过使用Omit类型省略每个级别上的当前键来确保路径中没有指定键两次:

type Diff<T extends string, U extends string> = ({ [P in T]: P } & { [P in U]: never } & { [x: string]: never })[T];
type Omit<T, K extends keyof T> = Pick<T, Diff<keyof T, K>>;


type Nested<T> = { [P in keyof T]?: Nested<Omit<T, P>> | number }


function mapTo<T>(arr: [T], basis: Nested<T>) {
    console.log('Hello!!!');
    console.log(basis);
}
class KeyValue {
    constructor(public key: string, public key1: string, public key3: string) {
    }
}

mapTo([new KeyValue('a', 'b', 'c')], { key: 1 }); //-> okay 
mapTo([new KeyValue('a', 'b', 'c')], { key: { key1: 1 } }); //-> okay 
mapTo([new KeyValue('a', 'b', 'c')], { key1: { key: 1 } }); //-> okay 
mapTo([new KeyValue('a', 'b', 'c')], { key1: { key: { key3: 1 } } }); //-> okay
mapTo([new KeyValue('a', 'b', 'c')], { key1: { key: { key4: 1 } } }); //-> error // key4 not exist
mapTo([new KeyValue('a', 'b', 'c')], { key: { key: 1 } }); //-> error, key appears twice