使用显式返回的undefined作为类型的guard和/或filter?

时间:2019-05-28 12:59:44

标签: typescript typeguards

我正在尝试将数组方法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

这可能是最好的折衷方案? 你怎么看?有没有我没有想到的解决方案,还是您妥协了?

2 个答案:

答案 0 :(得分:2)

@TitianCernicova-Dragomir's answer是正确的,但我也想给出一个略有不同的解决方案。主要区别在于,我们采用{{1}来代替conditional types来将S(可能包括undefined)转换成Exclude<S, undefined>(不包含)。 }作为返回类型(不包含S),并将输入类型用作undefined。从调用者的角度来看,它们的行为相同(或几乎如此)(编译器将对输入类型进行自己的类似S | undefined的分析),但是编译器将可能能够推理出在后一种情况下,Exclude的实现会更好:

malter

好的,希望能有所帮助。祝你好运!

Link to code

答案 1 :(得分:1)

您使用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
    );
}