来自字符串数组或对象的Typescript动态通用

时间:2018-06-27 05:21:50

标签: typescript

我想在下面喜欢

function factory<T>{
      // somethings happen
      return function(args:T){
   }
}


const methodType1 = factory<argType1>();
methodType1({a: 1, b: 2, c: 3}); // work
methodType1({d:4}); // not work

const argType2: string[] = ['d'];
const methodType2 = factory<argType2>();
methodType2({a: 1, b: 2, c: 3}); // not work
methodType2({d:4}); // work

有什么解决办法吗?还是不可能?

1 个答案:

答案 0 :(得分:1)

您可以创建一个工厂方法,该方法接受一个字符串数组并返回一个仅允许具有特定键的对象的函数:

function factory<T extends string>(fields: T[]) {
    // somethings happen
    return function (args: { [P in T]: number }) {
    }
}

const methodType2 = factory(["d"]); // T is 'd'
methodType2({ a: 1, b: 2, c: 3 }); // not work
methodType2({a: 1, b: 2, c: 3, d: 4}); //error 
methodType2({d:4}); // work

如果您不想直接在函数的参数中指定数组,则需要显式键入常量,因为编译器不会推断常量数组的字符串文字类型:

const argType2 = ['d']; // is string[] which losses information about the keys 
const argType2: Array<'d'> = ['d']; // better, we have the type information now

或者您可以使用辅助函数来创建数组:

function stringLiteralArray<T extends string>(...fields: T[]) { return fields; }
const argType2 = stringLiteralArray('d') // not explicit types needed is of type Array<'d'>

在上面的函数中,我将参数的所有键都键入为数字,如果需要更大的灵活性,则需要额外的一级功能,该功能允许我们首先指定键的类型。 (请注意,在3.0中,我们将只能指定部分通用参数,但到今天为止,在2.9版中,这是不可能的)

function factory<T extends string>(fields: T[]) {
    return {
        of<TValue>() {

            return function (args: { [P in T]: TValue }) {
            }
        }
    }
}
function stringLiteralArray<T extends string>(...fields: T[]) { return fields; }
const argType2 = stringLiteralArray('d') 
const methodType2 = factory(["d"]).of<number>();
methodType2({ a: 1, b: 2, c: 3 }); // not work
methodType2({a: 1, b: 2, c: 3, d: 4}); //error 
methodType2({d:4}); // work

此外,上面的函数还会在指定了更多属性的对象常量作为参数时引发错误,但是如果我们将该对象常量放入变量中,然后将其传递给函数,则这是合法的。

methodType2({ a: 1, b: 2, c: 3, d: 4 }); //error
let o = { a: 1, b: 2, c: 3, d: 4 }; 
methodType2(o); // ok 

如果要避免这种行为,您可以对生成的函数进行更多限制:

function factory<T extends string>(fields: T[]) {
    return {
        of<TValue>() {

            return function <V extends { [P in T]: number }>(args: V & { [P in Exclude<keyof V, T>]: never}) {
            }
        }
    }
}
function stringLiteralArray<T extends string>(...fields: T[]) { return fields; }
const argType2 = stringLiteralArray('d') 
const methodType2 = factory(["d"]).of<number>();
let o = { a: 1, b: 2, c: 3, d: 4 }; 
methodType2(o); // error  
methodType2({d:4}); // work