我创建了一个运算符“pluck”,它映射一个对象并返回其属性名称与提供给pluck的字符串列表匹配的值:
const pluck = (...args: string[]) => map(val => args.reduce((props, propName) => {
props[propName] = val[propName];
return props;
}, {}));
pluck('name', 'id', 'login');
有没有办法实现类型安全性以确保传递给pluck
操作的类型?我尝试过使用泛型:
const pluck = <T>(...args: string[]) => map<T>((val: T) => args.reduce((props, propName) => {
props[propName] = val[propName];
return props;
}, {}));
但是由于数组访问语法,这可能不起作用。但是,val.prop
不起作用,因为T
类型上的属性不存在。
此实现适用于数组:
const pluck = (arr, ...args) => arr.map(val => args.reduce((props, propName) => {
props[propName] = val[propName];
return props;
}, {}));
答案 0 :(得分:1)
不确定第一个map
示例中的pluck
是什么,但数组的类型版似乎
const pluck = <T, E extends keyof T>(arr: T[], ...args: E[]) => arr.map((val: T) => args.reduce((props, propName) => {
props[propName] = val[propName];
return props;
}, {} as {[e in E]: T[e]}));
interface Z {
x: number;
y: number;
name: string;
id: string;
login: string;
}
let z: Z[] = [];
let p = pluck(z, 'name', 'id', 'login');
// p's type is inferred as { name: string; id: string; login: string; }[]
诀窍是引入另一个通用参数E,限制为T
的一个键子集。此外,类型断言是将空对象转换为最终props
返回类型所必需的。然后,可以对props[propName]
进行类型检查 - props
已映射类型{[e in E]: T[e]}
,propName
已将E
作为其类型。
<强>更新强>
对于RxJX,我可以为完全通用的pluck
运算符提出最佳类型。它不好,因为它将属性类型推断为any
,但至少它会检查属性名称:
import {Observable} from "rxjs/Observable";
import "rxjs/add/operator/map";
import "rxjs/add/operator/let";
import 'rxjs/add/observable/from';
import {map, filter} from "rxjs/operators";
import {OperatorFunction} from "rxjs/interfaces";
const source = Observable.from([
{ name: 'Joe', age: 30, id: 123, login: 'joe1' },
{ name: 'Frank', age: 20, id: 456, login: 'frank1956' },
{ name: 'Ryan', age: 50, id: 2, login: 'z' }
]);
const pluck = <E extends string, T extends {[e in E]: T[e]}>(...args: E[]): OperatorFunction<T, {[e in E]: T[e]}> => map((val: T) => args.reduce((props, propName) => {
props[propName] = val[propName];
return props;
}, {} as {[e in E]: T[e]}));
const p = pluck('name', 'id', 'login');
const d = source.let(p); // Observable<{ name: any; id: any; login: any; }>
您可以添加另一级别的函数,并明确指定输入类型作为返回实际pluck
的函数的类型参数,但是:
type PluckOperatorType<T> = <E extends keyof T>(...args: E[]) => OperatorFunction<T, {[e in E]: T[e]}>;
const pluck = <T>(): PluckOperatorType<T> => <E extends keyof T>(...args: E[]) => map((val: T) => args.reduce((props, propName) => {
props[propName] = val[propName];
return props;
}, {} as {[e in E]: T[e]}));
interface Person {
name: string;
age: number;
id: number;
login: string;
}
const p = pluck<Person>()('name', 'id', 'login');
const d = source.let(p); // Observable<{ name: string; id: number; login: string; }>