【问题标题】:Define method's return type when using keyof使用 keyof 时定义方法的返回类型
【发布时间】:2017-05-12 22:26:50
【问题描述】:

想要在访问对象的属性之前创建包含一些对象并执行空/未定义检查的 Maybe 类。 当然,应该输入结果( Maybe 或 Maybe )。

这是一个例子:

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 在这里可以正常工作并输入。例如:

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。

  // 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

谢谢

【问题讨论】:

  • 我不清楚你的问题是什么。在您的游乐场代码中,您只是忘记声明类型参数Tresult。没关系,我看错了,我看到了问题。
  • 我觉得这个问题可以浓缩一点,现在还不是很清楚你想要什么。您似乎只是在询问使用keyof 时如何获取方法的返回类型。
  • 如果没有检测和确保 'f' 确实是函数名称(以某种方式表达 P extends keyof T, TR, T[P] extends (...args: any[]) =&gt; TR)的方法,我不确定这是否可行。

标签: typescript maybe


【解决方案1】:

首先,我先抛开Maybe&lt;T&gt;类,介绍一些基本操作,一步一步来。

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

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;

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

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 参数的确切文字类型。

【讨论】:

    猜你喜欢
    • 2020-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多