【问题标题】:How to write a type with optional unknown property name but known property type together with named and typed properties如何编写具有可选未知属性名称但已知属性类型以及命名和类型化属性的类型
【发布时间】:2019-05-07 02:59:04
【问题描述】:

我正在尝试写这样的东西。为了清楚起见,已对其进行了简化:

    interface $ {
        frameWorkMethod<T>(first: T, options: FrameworkMethodOptions<T>): object;
    }

    interface FrameworkMethodOptions<T> {
        aProperty?: string[];
        anotherProperty?: boolean;
        [P in keyof T]?: OptionsCallBack //THIS GIVES ERROR
    }

    interface OptionsCallBack {
        create?: () => void;
        update?: () => void;
    }

想法是一个有效的FrameworkMethodOptions&lt;T&gt;可以是:

  1. 一个空对象 { } 即没有属性,
  2. 具有一项或多项属性。
  3. 未定义/未知的命名属性(上述接口中的第三个属性)必须具有 T 的属性名称(P in keyof T)。

有效对象示例:

var user: User = { name: "leonardo", age: 33 };

interface User {
    name: string;
    age: number;
}

var example1: FrameworkMethodOptions<any> = {
    aProperty: ["this is string", " ignoreThis"]
};

var example2: FrameworkMethodOptions<any> = {
    aProperty: ["this is string", " ignoreThis"],
    age: { create: () => { console.log("ah") } }
};

var example3: FrameworkMethodOptions<User> = {
    aProperty: ["this is string", " ignoreThis"],
    age: { create: () => { console.log("ah") } }
};

var example4: FrameworkMethodOptions<User> = {
    aProperty: ["this is string", " ignoreThis"],
};

var example5: FrameworkMethodOptions<User> = {
    age: { create: () => { console.log("ah") } }
};

var example6: FrameworkMethodOptions<User> = {};
var example7: FrameworkMethodOptions<any> = {};

您可以在this Typescript Playground 中查看错误、我尝试和处理代码的其他替代方法

有一个问题:我使用的是 TS 2.3。但是,即使使用最新的 TS 版本,我也不认为这是可能的。 背景故事:Knockout Mapping 是 KnockoutJS 的一个插件。我正在尝试改进它的打字稿打字。

【问题讨论】:

    标签: typescript


    【解决方案1】:

    这应该有效,享受吧。

    如果有任何问题,请告诉我。

    type FrameworkMethodOptions2<T> = any extends T
        ? FrameworkMethodOptionsPart1
        : FrameworkMethodOptionsPart1 & FrameworkMethodOptionsPart2<T>
    

    【讨论】:

    • 嘿香农。这是一个不错的尝试,但我添加了更多示例来展示它不起作用的情况。我更新了问题。
    【解决方案2】:

    对我有用的解决方案

    interface FrameworkMethodOptionsPart1 {
        aProperty?: string[];
        anotherProperty?: boolean;
    }
    type FrameworkMethodOptionsPart2<T> = {
        [P in keyof T]?: OptionsCallBack
    }    
    type FrameworkMethodOptions<T> = FrameworkMethodOptionsPart1 | FrameworkMethodOptionsPart2<T>;
    

    对于未来的参考 porpuses,下面是我尝试过的所有替代方案,以防 Playground Link 停止工作。

    您需要将代码复制并粘贴到启用打字稿的 ide/代码编辑器中才能查看错误。

    //imagine this is the framework declarations
    interface $ {
        frameWorkMethod<T>(first: T, options: FrameworkMethodOptions<T>): object;
    }
    
    interface FrameworkMethodOptions<T> {
        aProperty?: string[];
        anotherProperty?: boolean;
        [P in keyof T]?: OptionsCallBack;
    }
    
    interface OptionsCallBack {
        create?: () => void;
        update?: () => void;
    }
    
    var user: User = { name: "leonardo", age: 33 };
    
    interface User {
        name: string;
        age: number;
    }
    
    var example1: FrameworkMethodOptions<any> = {
        aProperty: ["this is string", " ignoreThis"]
    };
    
    var example2: FrameworkMethodOptions<any> = {
        aProperty: ["this is string", " ignoreThis"],
        address: { create: () => { console.log("ah") } }
    };
    
    var example3: FrameworkMethodOptions<User> = {
        aProperty: ["this is string", " ignoreThis"],
        age: { create: () => { console.log("ah") } }
    };
    
    var example4: FrameworkMethodOptions<User> = {
        aProperty: ["this is string", " ignoreThis"],
    };
    
    var example5: FrameworkMethodOptions<User> = {
        age: { create: () => { console.log("ah") } }
    };
    
    var example6: FrameworkMethodOptions<User> = {};
    var example7: FrameworkMethodOptions<any> = {};
    
    var invalidExample1: FrameworkMethodOptions<User> = {
        address: { create: () => { console.log("ah") } } //Address ist not part of User interface
    };
    
    ////////////////////////////////////////////////////////////////////
    //B Alternative Idea
    
    interface FrameworkMethodOptionsPart1 {
        aProperty?: string[];
        anotherProperty?: boolean;
    }
    
    type FrameworkMethodOptionsPart2<T> = {
        [P in keyof T]?: OptionsCallBack
    }
    
    type FrameworkMethodOptionsB<T> = FrameworkMethodOptionsPart1 & FrameworkMethodOptionsPart2<T>;
    
    var example1b: FrameworkMethodOptionsB<any> = {
        aProperty: ["this is string", " ignoreThis"]
    };
    
    var example2b: FrameworkMethodOptionsB<any> = {
        aProperty: ["this is string", " ignoreThis"],
        address: { create: () => { console.log("ah") } }
    };
    
    var example3b: FrameworkMethodOptionsB<User> = {
        aProperty: ["this is string", " ignoreThis"],
        age: { create: () => { console.log("ah") } }
    };
    
    var example4b: FrameworkMethodOptionsB<User> = {
        aProperty: ["this is string", " ignoreThis"],
    };
    
    var example5b: FrameworkMethodOptionsB<User> = {
        age: { create: () => { console.log("ah") } }
    };
    
    var example6b: FrameworkMethodOptionsB<User> = {};
    var example7b: FrameworkMethodOptionsB<any> = {};
    
    var invalidExample1b: FrameworkMethodOptionsB<User> = {
        address: { create: () => { console.log("ah") } } //Address is not part of User interface
    };
    
    /////////////////////////////////////////////////////////////////
    // C Alternative Idea
    
    type FrameworkMethodOptionsC<T> = any extends T
        ? FrameworkMethodOptionsPart1
        : FrameworkMethodOptionsPart1 & FrameworkMethodOptionsPart2<T>;
    
    
    var example1c: FrameworkMethodOptionsC<any> = {
        aProperty: ["this is string", " ignoreThis"]
    };
    
    var example2c: FrameworkMethodOptionsC<any> = {
        aProperty: ["this is string", " ignoreThis"],
        address: { create: () => { console.log("ah") } }
    };
    
    var example3c: FrameworkMethodOptionsC<User> = {
        aProperty: ["this is string", " ignoreThis"],
        age: { create: () => { console.log("ah") } }
    };
    
    var example4c: FrameworkMethodOptionsC<User> = {
        aProperty: ["this is string", " ignoreThis"],
    };
    
    var example5c: FrameworkMethodOptionsC<User> = {
        age: { create: () => { console.log("ah") } }
    };
    
    var example6c: FrameworkMethodOptionsC<User> = {};
    var example7c: FrameworkMethodOptionsC<any> = {};
    
    var invalidExample1c: FrameworkMethodOptionsC<User> = {
        address: { create: () => { console.log("ah") } } //Address is not part of User interface
    };
    
    /////////////////////////////////////////////////////////////////
    // D Alternative Idea
    
    type FrameworkMethodOptionsPart2Untyped = {
        [key: string]: OptionsCallBack     // TS DOESNT ALLOW THIS TO BE OPTIONAL
    }
    
    type FrameworkMethodOptionsD<T> = any extends T
        ? FrameworkMethodOptionsPart1 & FrameworkMethodOptionsPart2Untyped
        : FrameworkMethodOptionsPart1 & FrameworkMethodOptionsPart2<T>;
    
    var example1d: FrameworkMethodOptionsD<any> = {
        aProperty: ["this is string", " ignoreThis"]
    };
    
    var example2d: FrameworkMethodOptionsD<any> = {
        aProperty: ["this is string", " ignoreThis"],
        address: { create: () => { console.log("ah") } }
    };
    
    var example3d: FrameworkMethodOptionsD<User> = {
        aProperty: ["this is string", " ignoreThis"],
        age: { create: () => { console.log("ah") } }
    };
    
    var example4d: FrameworkMethodOptionsD<User> = {
        aProperty: ["this is string", " ignoreThis"],
    };
    
    var example5d: FrameworkMethodOptionsD<User> = {
        age: { create: () => { console.log("ah") } }
    };
    
    var example6d: FrameworkMethodOptionsD<User> = {};
    var example7d: FrameworkMethodOptionsD<any> = {};
    
    var invalidExample1d: FrameworkMethodOptionsD<User> = {
        address: { create: () => { console.log("ah") } } //Address is not part of User interface
    };
    
    /////////////////////////////////////////////////////////////////
    // E Alternative Idea
    
    type FrameworkMethodOptionsE<T> = FrameworkMethodOptionsPart1 & FrameworkMethodOptionsPart2Untyped
    
    var example1e: FrameworkMethodOptionsE<any> = {
        aProperty: ["this is string", " ignoreThis"]
    };
    
    var example2e: FrameworkMethodOptionsE<any> = {
        aProperty: ["this is string", " ignoreThis"],
        address: { create: () => { console.log("ah") } }
    };
    
    var example3e: FrameworkMethodOptionsE<User> = {
        aProperty: ["this is string", " ignoreThis"],
        age: { create: () => { console.log("ah") } }
    };
    
    var example4e: FrameworkMethodOptionsE<User> = {
        aProperty: ["this is string", " ignoreThis"],
    };
    
    var example5e: FrameworkMethodOptionsE<User> = {
        age: { create: () => { console.log("ah") } }
    };
    
    var example6e: FrameworkMethodOptionsE<User> = {};
    var example7e: FrameworkMethodOptionsE<any> = {};
    
    var invalidExample1e: FrameworkMethodOptionsE<User> = {
        address: { create: () => { console.log("ah") } } //Address is not part of User interface
    };
    
    /////////////////////////////////////////////////////////////////
    // F Alternative Idea. THIS ONE WORKS. !!!!!!!!!!!!!!!!!!!!!!!!!
    
    type FrameworkMethodOptionsF<T> = FrameworkMethodOptionsPart1 | FrameworkMethodOptionsPart2<T>;
    
    var example1f: FrameworkMethodOptionsF<any> = {
        aProperty: ["this is string", " ignoreThis"]
    };
    
    var example2f: FrameworkMethodOptionsF<any> = {
        aProperty: ["this is string", " ignoreThis"],
        address: { create: () => { console.log("ah") } },
        anythingGoes: 42
    };
    
    var example3f: FrameworkMethodOptionsF<User> = {
        aProperty: ["this is string", " ignoreThis"],
        age: { create: () => { console.log("ah") } }
    };
    
    var example4f: FrameworkMethodOptionsF<User> = {
        aProperty: ["this is string", " ignoreThis"],
    };
    
    var example5f: FrameworkMethodOptionsF<User> = {
        age: { create: () => { console.log("ah") } }
    };
    
    var example6f: FrameworkMethodOptionsF<User> = {};
    var example7f: FrameworkMethodOptionsF<any> = {};
    
    var invalidExample1f: FrameworkMethodOptionsF<User> = {
        address: { create: () => { console.log("ah") } } //Expect error. Address is not part of User interface
    };
    
    var invalidExample2f: FrameworkMethodOptionsF<User> = {
        age: { create: () => { console.log("ah") } },
        address: { create: () => { console.log("ah") } } //Expect error. Address is not part of User interface
    };
    
    var invalidExample3f: FrameworkMethodOptionsF<User> = {
        aProperty: ["this is string", " ignoreThis"],
        address: { create: () => { console.log("ah") } } //Expect error. Address is not part of User interface
    };
    
    var invalidExample4f: FrameworkMethodOptionsF<User> = {
        aProperty: 42, //Expect error. aProperty's type is  string[]
    };
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-11-04
      • 2022-11-24
      • 1970-01-01
      • 2016-08-21
      • 1970-01-01
      • 2020-08-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多