可能的未定义属性的索引类型

时间:2018-02-25 05:27:34

标签: typescript

我正在尝试使用advanced types编写一个函数,该函数在给定类型及其任意两个键的情况下,必须返回由两个键确定的子类型的实例。例如:

class MyClass {
    id: string
    name: string
    age: number 
}


const x = subtype(MyClass, 'id', 'name') 
// x type should be { id: string, name: string }

function subtype<Class, Key1 extends keyof Class, Key2 extends keyof 
    Class>(
    constructor: {new(): Class},
    key1: Key1,
    key2?: Key2,
): Pick<Class, Key1 | Key2> {
    const instance = new constructor()
    if (!key2) {
        return pick<Class, Key1 | Key2>(instance, key1)
    } else {
        return pick<Class, Key1 | Key2>(instance, key1, key2)
    }
} 
// the function above is my current implementation, not working

问题是,因为'key2'参数是可选的,typescript无法使用Pick映射类型正确地确定类型,结果是x具有原始类的所有属性,如下所示:

const x = builder(MyClass, 'id', 'name') 
// x type is { id: string, name: string, age: number } (Wrong!)

拜托,有人可以给我一个帮助吗?我真的需要保持key1和key2彼此独立,我还需要key2是可选的,所以做的事情如下:

function builder<Class, Key extends keyof Class>(
    constructor: {new(): Class},
    keys: Key[],
): Pick<Class, Keys> { ... }

不是我的选择。

1 个答案:

答案 0 :(得分:0)

对于你的第一个函数,问题是如果你不提供type参数,它将被推断为最大类型(在这种情况下是T的任何键),所以Pick将最终包含所有属性。您可以提供带有和不带可选参数的多个签名:

function subtype<Class, Key1 extends keyof Class>(constructor: {new(): Class},key1: Key1): Pick<Class, Key1> 
function subtype<Class, Key1 extends keyof Class, Key2 extends keyof Class>
    (constructor: {new(): Class},key1: Key1,key2: Key2): Pick<Class, Key1 | Key2> 
function subtype<Class, Key1 extends keyof Class, Key2 extends keyof Class>
    (constructor: {new(): Class},key1: Key1,key2?: Key2): Pick<Class, Key1 | Key2> {
    const instance = new constructor()
    if (!key2) {
        return pick<Class, Key1 | Key2>(instance, key1)
    } else {
        return pick<Class, Key1 | Key2>(instance, key1, key2)
    }
} 

const x = subtype(MyClass, 'id', 'name') // will have only id and name
const x2 = subtype(MyClass, 'id')  // will have just id

至于第二个例子,它在语法上是不正确的,你有一个数组参数,你不用数组调用。您可以使用其余的parmaters,它会正确推断:

function builder<Class, Key extends keyof Class>(
    constructor: {new(): Class},
    ...keys: Key[],
): Pick<Class, Key> { return  null; }


const xw = builder(MyClass, 'id', 'name')  // will have only id and name