使用keyof

时间:2017-05-12 22:26:50

标签: typescript maybe

想创建Maybe< T>在访问对象的属性之前,将包含某个对象并执行null / undefined检查的类。 当然,应该输入结果(可能< null>或者可能< TRes>)。

以下是一个例子:

class Maybe < T > {
  constructor(public value: T) {}
  static of < P > (obj: P): Maybe < P > {
    return new Maybe(obj);
  }

  map < TResult > (fn: (arg: T) => TResult): Maybe < TResult > {
    return this.isNothing ? nothing : new Maybe(fn(this.value));
  }

  public get < P extends keyof T > (name: P): Maybe < T[P] > {
    return this.isNothing ? nothing : Maybe.of(this.value[name]);
  }
  get isNothing(): boolean {
    return this.value == null;
  }
}
let nothing = new Maybe(null);

Everethisng在这里工作正常并打字。对于exaple:

class Test {
  a = {
    id: 1,
    name: "test1"
  };
  f = (foo: string) => {
    return new Test();
  };
};

let t = new Test();

console.log(Maybe.of(t).get('a').get('name').value); // ok 

但是定义“apply”函数有问题,它接受对象属性的名称,实际上是一个函数,执行该函数并返回Maybe&lt; TResult&gt;。

  // T[fName]: (...args)=> TResult
  public apply < P extends keyof T > (fnName: P, ...args: any[]) /*: Maybe<Tresult> */ {
    if (!this.isNothing) {
      let res = null;
      let fn = this.value[fnName];

      if (isF(fn)) {
        return Maybe.of(fn(...args));
      }

    }
    return nothing;
  }

无法找到定义“应用”调用结果的解决方案。

let fResult = Maybe.of(t).apply('f', 'foo');
// fResult is Maybe<any> ,  expected to be Maybe<Test>

有人知道如何定义“申请”结果的类型吗?或者甚至可以在TS 2.3中实现这一点?

这里有相同代码的游乐场上的链接: TS playground

由于

1 个答案:

答案 0 :(得分:0)

首先,让我把Maybe<T>课放在一边,一步一步地介绍一些基本操作。

您可以使用泛型函数来检查对象是否为null,然后返回该对象的某些属性。函数的返回类型可以从属性的类型推断出来:

function maybeGet<T, N extends keyof T>(t: T | undefined, n: N) {
  return t ? t[n] : undefined;
}

使用Test班级

class Test {
  a = {
    id: 1,
    name: "test1"
  };
  f = (foo: string) => {
    return new Test();
  };
};

let t = new Test();

你可以像这样使用它

const a = maybeGet(t, 'a');

确实a的类型被推断为{ id: number; name: string; }

然后,您可以定义描述某些函数的泛型类别别名,返回类型为R

type FR<R> = (...args: any[]) => R;

然后定义将接受另一个函数的泛型函数,如果它不为null则调用它。函数的返回类型是从其参数的返回类型推断出来的:

function maybeApplyFunction<R>(f: FR<R> | undefined, ...args: any[]) {
  return f ? f(...args) : undefined; 
}

const r = maybeApplyFunction(t.f, 'foo'); // r has type 'Test'

你可以明确地将两者结合在一起,没有问题

const tf = maybeApplyFunction(maybeGet(t, 'f'), 'foo'); 
// tf has type `Test`

问题是将两者结合在一个通用操作中。

使用映射类型,您可以尝试为具有返回R作为属性的函数的对象定义类型别名

type FM<N extends string, R> = {[n in N]: FR<R>};

并使用

编写泛型函数
function maybeApplyMemberFunction<N extends string, R>(o: FM<N, R>, n: N, ...args: any[]) {
  return o ? o[n](...args) : undefined;
}

它甚至适用于某些情况

class T1 { f() { return new T1() } };

const b = maybeApplyMemberFunction(new T1(), 'f');
// b has type T1

但是,它不适用于您的测试,因为TypeScript由于某种原因将使用keyof T作为N,并坚持Test中的所有属性都必须是可调用的:

const tm = maybeApplyMemberFunction(t, 'f');

// Argument of type 'Test' is not assignable to parameter of type 
// 'FM<"a" | "f", Test>'.
  // Types of property 'a' are incompatible.
    // Type '{ id: number; name: string; }' is not assignable to type 'FR<Test>'.
      //  Type '{ id: number; name: string; }' provides no match for the signature
      // '(...args: any[]): Test'.

code in playground

如果您将N限制为精确的文字类型,则可以使用:

function maybeApplyMemberFunctionF<N extends 'f', R>(o: FM<N, R>, n: N, ...args: any[]) {
  return o ? o[n](args) : undefined;
}
const t1 = maybeApplyMemberFunctionF(t, 'f');

不幸的是,typescript没有办法指定N应该是n参数的确切文字类型。