输入'{name:string; }&Pick <tprops,排除<keyof =“” tprops,=“”名称“ =”“ >>'不能分配给'TProps'类型

时间:2018-12-11 14:38:53

标签: typescript

我正在尝试创建一个React HoC来设置一些道具(如果没有提供的话)。

我可以根据需要提供该代码,但是它带有一些React包,这些代码使代码变得复杂,并且实际上与我遇到的错误没有任何关系。

因此,我创建了一个MVCE,可以更清晰地重现该问题:

props

name是不可变的,因此如果它以undefined的形式出现在if (props.name == null) { props.name = "alice"; } 中,那么我将无法做到:

...rest

我得到了错误:

  

TypeError: Cannot add property XXX, object is not extensible

因此,我改为使用object destructuring来获取我知道应该存在的字段,并将其余字段放在newProps中。

然后,我构造...rest并将我默认的字段与{ name: string; } & Pick<TProps, Exclude<keyof TProps, "name">>结合起来。

问题在于,此时我得到了错误:

  

类型TProps不可分配给类型name

对我来说,这是“ TProps,不包括name属性以及name字段,不能分配给TProps”,这让我有些困惑。

我想知道是否不喜欢string现在是string?而不是string。问题是没有道理的,因为string?可分配给const newProps: TProps = { name: ((nameProp != null) ? nameProp : "Alice") as string | undefined, ...rest }; ,但是我还是尝试修改代码。

{ name: string; }

但是,正如预期的那样,没什么区别。

我注意到错误消息中指出类型是{ name?: string; }而不是const newRequiredProps: { name?: string } = { name: (nameProp != null) ? nameProp : "Alice" }; const newProps: TProps = { ...newRequiredProps, ...rest }; ,所以我进行了代码更改以尝试解决该问题。

{ name?: string; } & Pick<TProps, Exclude<keyof TProps, "name">>

现在,错误消息显示了“更正确”的类型,但基本上保持不变。

  

类型TProps不可分配给类型as

我确实发现了一件可行的事情,那就是使用const newProps: TProps = { name: (nameProp != null) ? nameProp : "Alice", ...rest } as TProps; 强制类型:

{{1}}

但是那感觉就像是我只是在掩盖打字问题,而没有真正解决它。

有没有办法让类型系统理解我要做什么,而不是强迫它接受类型?

1 个答案:

答案 0 :(得分:1)

对于编译器来说,很难验证对泛型类型的复杂条件类型操纵。只要您确定不会发生任何疯狂的事情,那么您正在执行的类型断言是完全可以接受的:

type PossiblyBob = { name?: "Bob" };
const possiblyBob: PossiblyBob = Math.random() < 0.5 ? {} : { name: "Bob" };
setDefaults(possiblyBob);

请注意,类型PossiblyBob具有name属性,该属性必须是字符串"Bob"undefined。当您调用setDefaults(possiblyBob)时,它会将TProps实例化为PossiblyBob。但是在您的setDefaults()实现中,newProps在运行时有50%的机会成为{name: "Alice"},这不是PossiblyBob。哦,很好。

但是,假设不会发生这种极端情况,您可以告诉编译器不要担心,并使用类型断言。

说不需要类型声明的同一件事的一种可能更简短的方法是使用Object.assign()

const setDefaults: TProps = <TProps extends RequiredProps>(props: TProps) => {            
  const newProps = Object.assign({ name: "Alice" }, props);      
};

这差不多是同一件事(只要props.name实际上没有设置为值undefined)。但是,缺少类型错误是一种技巧。 Object.assign()的{​​{3}}返回其输入类型的standard library typings,即intersection。最终,它们可能会not technically true的返回类型Object.assign(),然后您在那里也会遇到相同的类型断言问题。

所以我想我的建议是离开类型断言并继续。

希望有所帮助;祝你好运!