泛型函数和索引类型

时间:2018-11-14 11:58:54

标签: typescript

我正在寻求实现一个通用函数,该函数接受单个类型参数“ T”,它是一个对象。它使用keyof T指定函数“ property”的第一个参数。现在我无法工作的部分,我想使用T[typeof property]的类型来指定函数“值”的第二个参数的类型。

这使我接近理想的用法,

type Person = {
  name: string;
  age: number;
};

function filter<T>(property: keyof T, value: T[typeof property]) {

 return `${property} ${value}`

}

filter<Person>("name", 123);

通过上面的代码,我们对“属性”参数进行了类型检查,将其正确地限制为“名称”或“年龄”。不幸的是,对于value参数,它接受数字或字符串,因此它似乎正在创建所有Person类型的keyof的并集。

有什么想法吗?

2 个答案:

答案 0 :(得分:1)

您需要在两个参数之间创建一个关系,毕竟您可能要传递多个键和多个值,为什么编译器会假设value应该具有在property参数。

要创建关系,您将需要一个额外的类型参数以将属性表示为文字类型

function filter<T, K extends keyof T>(property: K, value: T[K]) {
   return `${property} ${value}`
}

filter<Person, "name">("name", 123); // error
filter<Person, "age">("age", 123); // ok

上述实现的问题是,由于打字稿不支持部分类型推断,因此您必须指定其他类型参数。 (希望issue提出的建议很快就会实现,原定于2018年1月推出,但已经推迟了好几次)

为解决此不便,我们可以创建一个函数,该函数返回一个函数并在第一个调用中修复T参数,并在第二个调用中推论K

function filter<T>() {
  return function <K extends keyof T>(property: K, value: T[K]) {
    return `${property} ${value}`
  }

}
filter<Person>()("name", 123); // error
filter<Person>()("age", 123); // ok

或者根据您打算对filter进行的操作,可以为函数保留两个类型参数,并将返回类型用作推断T的源:

function filter<T, K extends keyof T>(property: K, value: T[K]) {
  return `${property} ${value}` as IFiler<T>
}

type IFiler<T> = string & { _isFilterFor: T } 
class Query<T> {
  addFilter(f: IFiler<T>) {

  }
}

var q = new Query<Person>();
// T will be inferred to Person in filter because we assign in to a parameter expecting IFilter<Person>
q.addFilter(filter('age', "")) //error 
q.addFilter(filter('age', 12)) //ok 
// Same as above but assigning to a variable:
var f: IFiler<Person> = filter('age', 12);
var f2: IFiler<Person> = filter('age', "12");

答案 1 :(得分:1)

通过这种方式,您不必更改代码

type Fn<T> = <K extends keyof T>(prop: K, value: T[K]) =>  string;

function filter(property: string, value: any) {
  return `${property} ${value}`;
}

type Person = {
  name: string;
  age: number;
};

const result = (filter as Fn<Person>)("name", 123);