我尝试将数组方法filter和map组合到一个名为malter的函数中。我已经走了这么远:
type mapFn<T, S> = (value: T, index: number, originalArray: Readonly<T[]>) => S;
interface Array<T> {
malter<S = any>(mapFn: mapFn<T, S>): S[];
}
function notUndefined<T>(v: T | undefined): v is T {
return typeof v !== "undefined"
}
Array.prototype.malter = function malter<T, S = any>(mapFn: mapFn<T, S>): S[] {
return this.reduce(function(acc: S[], val: T, index: number, orgArr: T[]) {
const el = mapFn(val, index, orgArr);
if (notUndefined(el)) {
acc.push(el);
}
return acc;
}, []);
};它基本上是有效的。但在使用它时,它会在第5-7行抛出一个TypeError。另一个测试函数确实隐式返回了undefined,也抛出了这个错误。
Type 'string | undefined' is not assignable to type 'string'.
Type 'undefined' is not assignable to type 'string'."const test = [2, 3, 4, 5, 3];
function test1(): string[] {
return test.malter<number, string>(num =>
num > 3
? num.toFixed(2)
: undefined
);
}一个有效的解决方案是为malter提供2个参数。一个过滤器和一个映射函数,并分别调用它们。这将确保类型的有效性,但也会使它变得不那么简单。
当然,我可以简单地在第5-7行这样做:
(num > 3 ? num.toFixed(2) : undefined) as string这可能是最好的折衷方案?你认为如何?有没有我没有想到的解决方案,或者你愿意妥协?
发布于 2019-05-28 21:12:21
您正在使用undefined作为要筛选的值。我们可以允许内部函数返回undefined,然后使用Exclude过滤掉它,就像实现所做的那样:
type mapFn<T, S> = (value: T, index: number, originalArray: Readonly<T[]>) => S;
interface Array<T> {
malter<S>(mapFn: mapFn<T, S>): Exclude<S, undefined>[];
}
function notUndefined<T>(v: T | undefined): v is T {
return typeof v !== "undefined"
}
Array.prototype.malter = function malter<T, S>(mapFn: mapFn<T, S>): Exclude<S, undefined>[] {
return this.reduce(function (acc: S[], val: T, index: number, orgArr: T[]) {
const el = mapFn(val, index, orgArr);
if (notUndefined(el)) {
acc.push(el);
}
return acc;
}, []);
};
const test = [2, 3, 4, 5, 3];
function test1(): string[] {
return test.malter(num =>
num > 3
? num.toFixed(2)
: undefined
);
}发布于 2019-05-28 22:03:36
@TitianCernicova-Dragomir's answer是正确的,但我也想给出一个稍微不同的解决方案。主要区别在于,不是使用conditional types将S (可能包括undefined)转换为Exclude<S, undefined> (不包括),我们将S作为返回类型(不包括undefined),并使用输入类型作为S | undefined。从调用者的角度来看,它们的行为是相同的(或几乎相同)(编译器将对输入类型进行自己的Exclude-like分析),但在后一种情况下,编译器很可能能够更好地推断malter实现中的类型:
type mapFn<T, S> = (value: T, index: number, originalArray: Readonly<T[]>) => S;
interface Array<T> {
// S will not include undefined when inferred from mapFn
malter<S>(mapFn: mapFn<T, S | undefined>): S[];
}
// hey, look, this works the same way, with T|undefined => T
// instead of T => Exclude<T, undefined>
function notUndefined<T>(v: T | undefined): v is T {
return typeof v !== "undefined";
}
Array.prototype.malter = function malter<T, S>(
this: Array<T>, // inform the compiler that this is an array
mapFn: mapFn<T, S | undefined>
): S[] {
return this.reduce(function(acc: S[], val: T, index: number, orgArr: T[]) {
const el = mapFn(val, index, orgArr);
if (notUndefined(el)) {
acc.push(el);
}
return acc;
}, []);
}; // inference in implementation works
const test = [2, 3, 4, 5, 3];
// don't need any type parameters
function test1(): string[] {
return test.malter(num => (num > 3 ? num.toFixed(2) : undefined));
}好吧,希望这能帮上忙。祝好运!
https://stackoverflow.com/questions/56342876
复制相似问题