如何编写具有可选未知属性名称但已知属性类型以及命名和类型化属性的类型

时间:2019-04-23 19:58:20

标签: typescript

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

    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<T>可以是:

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

有效对象的示例:

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映射是KnockoutJS的插件。我正在尝试改进它的打字稿打字。

2 个答案:

答案 0 :(得分:0)

这应该工作,请享受。

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

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

答案 1 :(得分:0)

对我有用的解决方案

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

对于将来的参考豚鼠,下面是我尝试过的所有替代方法,以防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[]
};
相关问题