为什么这两种类型在未定义检查后无法统一?

问题描述 投票:0回答:1

我有以下内容

export type WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] };

type A = {
  f1?: string;
};

function test(a: A): WithRequired<A, 'f1'> {
  if (a.f1) {
    return a;
  } else {
    throw Error();
  }
}

但是,这会产生错误

Type 'A' is not assignable to type 'WithRequired<A, "f1">'.
  Type 'A' is not assignable to type '{ f1: string; }'.
    Types of property 'f1' are incompatible.
      Type 'string | undefined' is not assignable to type 'string'.
        Type 'undefined' is not assignable to type 'string'.

我想我希望它能起作用,因为在这个分支的上下文中,编译器知道 a.f1 不是未定义的......并且在某些上下文中,打字稿正确地执行此操作。

我想我有两个问题

  1. 为什么这不起作用? (更好地理解编译器)
  2. 有没有办法在没有演员的情况下制作类似的作品?
typescript
1个回答
0
投票

'a' 不是您返回的类型,因此您必须在代码中对其进行强制转换。另一种方法是使用 type 谓词:

type WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] };

type A = {
  f1?: string;
};

function isWithRequired<T, K extends keyof T>(obj: T, k: K): obj is WithRequired<T,K> {
  return Boolean(obj[k]);
}

function test(a: A): WithRequired<A, 'f1'> {
  if(isWithRequired(a, 'f1')){
    return a;
  } else {
    throw Error();
  }
}

function testF<T,K extends keyof T>(obj: T, k: K): WithRequired<T,K> {
  if(isWithRequired(obj, k)) {
    return obj;
  } else {
    throw Error();
  }
}

const a1:A = {};
const a2:A = {f1: 'xyz'};

try {
  const test1 = testF(a1, 'f1');
  console.log('>> test1: ', test1);
} catch {
  console.log('test1 failed');
}

try {
  const test2 = testF(a2, 'f1');
  console.log('>> test2: ', test2);
} catch {
  console.log('test2 failed');
}


一旦进入类型谓词的 if 块,编译器就会假定该谓词类型。

我修改了你的原始函数并添加了第二个

testF
,它是通用的,也就是说,你可以向它传递对象和字段(而不是特定于类型“A”)

© www.soinside.com 2019 - 2024. All rights reserved.