【问题标题】:TypeScript Conditional Type complains Type not assignableTypeScript 条件类型抱怨类型不可分配
【发布时间】:2019-04-11 21:56:02
【问题描述】:

我正在尝试了解 TypeScript 条件类型的工作原理。这是我的代码。有类型错误:

interface MyType {
  name: string;
}

const testFunc = <T extends MyType | string>(
  what: T
): T extends MyType ? MyType : string => {
  if (typeof what === 'object') {
    return what['name'];
  }
  return what;
};

正确的用法是什么?

【问题讨论】:

标签: typescript


【解决方案1】:

代码中的函数TestFunc 应该在任何情况下都返回string。我认为这是一种错字。让我们修复它并继续。

后来我想出了一个更安全的解决方案(我将旧答案留在了底部)。最好使用重载。在重载中描述条件逻辑,在函数中使用联合类型。

interface MyType {
  name: string;
}

function testFunc<T extends MyType | string>(
  what: T
): T extends MyType ? string : MyType;

function testFunc(what: MyType | string): MyType | string {
  if (typeof what === 'object') {
    return what.name;
  }
  return { name: what };
}

旧答案:

interface MyType {
  name: string;
}

type TestFunc = <T extends MyType | string>(what: T) => T extends MyType ? string : MyType;

const testFunc: TestFunc = (what: any) => {
  if (typeof what === 'object') {
    return what.name;
  }
  return { name: what };
};

或者如果你更喜欢定义内联类型:

interface MyType {
  name: string;
}

const testFunc: <T extends MyType | string>(what: T) =>
  T extends MyType ? string : MyType =
  (what: any) => {
    if (typeof what === 'object') {
      return what.name;
    }
    return { name: what };
  };

Typescript 编译器会这样处理它:

const a1: MyType = testFunc({ name: 'foo' }); // Type 'string' is not assignable to type 'MyType'.

const a2: MyType = testFunc({ name: 1 }); // Error: Argument of type '{ name: number; }'
//                                is not assignable to parameter of type 'string | MyType'

const a3: string = testFunc({ name: 'foo' }); // Ok

const a4: string = testFunc('foo'); // Error: Type 'MyType' is not assignable to type 'string'.

const a5: MyType = testFunc('foo'); // Ok

【讨论】:

  • 重载的新逻辑对我来说效果很好。很好的发现!
【解决方案2】:

这个答案基本上是在用更多的文字和代码解释@jcalz的comment

您正确理解了这个概念。不幸的是,您在 TS 中遇到了一个警告,在通过控制流分析缩小可能性时,它并没有平等对待具体类型和泛型类型。

理想情况下,您建议的用法应该是有效的。不过TSdoesn't support it yet.

现在我们需要解决问题,这是我通常会做的。

interface MyType {
  name: string;
}

const testFunc = <T extends MyType | string>(
  _what: T
): T extends MyType ? MyType : string => {
  // First thing to do once enter the fn body,
  // we manually cast to `any` type
  var what = _what as any;
  if (typeof what === 'object') {
    return what['name'];
  }
  return what;
};

不完美,我知道。这有点像你实现了一个重载函数,最终你只需要使用any 类型。但是既然你已经给你的消费者提供了一个完善的函数接口,那么在后台有点脏也没关系。

【讨论】:

    【解决方案3】:

    我会这样做:

    interface MyType {
      name: string;
    }
    
    const testFunc = <T>(what: T): T extends MyType ? MyType : string => {
        if (typeof what === 'object') {
            return what['name'];
        } 
        return what as any;
    
    };
    

    as any 表示“TypeScript,不要抱怨这种类型”。问题是what的窄化类型没有被条件类型拾取,因此函数无法评估条件并将返回类型窄化为what

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-08-12
      • 2020-01-02
      • 2020-06-09
      • 2020-11-12
      • 2021-06-27
      • 2018-09-19
      • 2022-08-23
      • 1970-01-01
      相关资源
      最近更新 更多