代码之家  ›  专栏  ›  技术社区  ›  Alexander Abakumov

声明两个不相关类型的公共属性名的编译器可检查数组

  •  1
  • Alexander Abakumov  · 技术社区  · 7 年前

    class Class1 {
        public prop1: number;
        public prop2: number;
        public prop3: number;
    }
    

    我宣布 ReadOnlyArray Class1 钥匙:

    private readonly keysSubset: ReadOnlyArray<keyof Class1> = ["prop1", "prop2"];
    

    现在,我想声明一个变量,它可以接受 keysSubset

    const someKey: ? = this.keysSubset[1];
    

    我也不想给你 string 键入或手动枚举以下可能的值:

    const someKey: "prop1" | "prop2" = this.keysSubset[1];
    

    我想明确地说“这可以包含 键子集 “,所以当项目 键子集 someKey 手动声明类型,编译器仍然可以检查 某某 .

    更新 (提供更多关于理由的信息):
    我需要准确的型号 某某 迭代 键子集 Class2 从类型的对象 一级 :

    class Class2 {
        public prop1: number;
        public prop2: number;
    
        private readonly keysSubset: ReadOnlyArray<keyof Class1> = ["prop1", "prop2"];
    
        public updateFrom(source: Class1): void {
            this.keysSubset.forEach((someKey: ?/*keyof Class1 won't work since Class2 doesn't have prop3*/): void => {
                    this[someKey] = source[someKey];
                });
        }
    }
    

    简而言之,我要解决的问题是更新给定的 从不同类型的实例 第二类 . 虽然子集是静态的,但是每个属性更新都有一些通用逻辑,所以我不想只编写一堆 class1[someLey] = class2[someLey] 声明。

    某某 在这种情况下?

    2 回复  |  直到 7 年前
        1
  •  1
  •   Titian Cernicova-Dragomir    7 年前

    问题是 keysSubset 是类中所有键的数组。这是我们首先需要改变的,以便 键子集 正是一个赋值数组。我们可以使用helper函数来实现这一点,同时仍然将值约束为类的键。

    class Class1 {
        public prop1: number;
        public prop2: number;
        public prop3: number;
    }
    // Helper function to infer the subset of keys correctly 
    function keyArrayOf<T>() {
        return function <K extends keyof T>(o: K[]) {
            return o;
        }
    }
    
    class Foo {
        // will be inferred as  ("prop1" | "prop2")[]
        private readonly keysSubset = keyArrayOf<Class1>()(["prop1", "prop2"])
        method(){
            // Use a type query to get the type of an item of the keysSubset array
            const someKey: Foo['keysSubset'][number] = this.keysSubset[1]; // "prop1" | "prop2"
            // Or just let inference work
            const someKeyInffered = this.keysSubset[1]; // "prop1" | "prop2", no need for an explicit annotation 
        }
    }
    

    编辑

    keyArrayOf 助手函数为:

    class Class2 {
        public prop1: number;
        public prop2: number;
    
        private readonly keysSubset = keyArrayOf<Class1>()(["prop1", "prop2"])
    
        public updateFrom(source: Class1): void {
            this.keysSubset.forEach((someKey: Class2['keysSubset'][number]): void => {
                this[someKey] = source[someKey];
            });
        }
    }
    
        2
  •  0
  •   Alexander Abakumov    7 年前

    ( post 包含对我最初问题的直接回答。此答案包含对 . 多亏了@Titian,我才意识到我在反省:我不需要建立一个联盟 . 我需要一个工会 由来自的公共属性名组成的类型 Class1 Class2 首先。然后,我就可以声明这种类型的数组了。所以,下面包含了这个问题的答案。)

    TLDR公司

    下面是如何声明仅包含其他两种类型的公共属性的类型:

    type CommonProperty = keyof {
      [P in keyof Class1 & keyof Class2]: Class1[P] | Class2[P]
    }
    

    现在,您可以声明一个数组,该数组包含所需的公共属性子集,编译器将检查这些属性值的正确性:

    const keysSubset: ReadOnlyArray<CommonProperty> = [
            "prop1" // Ok,
            "prop2" // Ok,
            "prop4" // Error - property is missing in Class1
        ];
    

    第一步是理解如何声明一个类型,该类型只包含来自类型的公共属性 第二类 :

    type CommonFromClass1AndClass2 = {
      [P in keyof Class1 & keyof Class2]: Class1[P] | Class2[P]
    }
    

    现在我们可以声明一个表示公共属性名子集的字符串联合类型:

    type CommonProperty = keyof CommonFromClass1AndClass2;
    

    最后,我们可以在数组声明中使用它:

    const keysubset:ReadOnlyArray<CommonProperty>=[
    “prop1“//Ok,
    “prop2“//Ok,
    “prop4”//错误-Class1中缺少属性
    ];
    
    推荐文章