首页 > 解决方案 > 部分的联合类型和字典导致打字问题没有隐含的任何标志

问题描述

由于可以使用 VueJSv-bind:style绑定设置 CSS 变量,我正在尝试创建一个联合类型,其中传递给v-bind:style的对象保留类型,CssStyleDelcaration但足够宽松以接受任意属性名称:

type Dictionary<T> = { [key: string]: T };
type Nullable<T> = T | null;
type CssStyleObject =
  Partial<CSSStyleDeclaration> |
  Dictionary<Nullable<string>>;

一个实现的例子是这样的(参见 TypeScript playground 上的实时代码):

<!-- Template -->
<div v-bind:style="myStyle"></div>
// Component code
@Component
export default class MyComponent extends Vue {
  public get myStyle(): CssStyleObject {
    const style: CssStyleObject = { };
    style.backgroundColor = 'red';
    style['--my-custom-css-property'] = '16px';
  }
}

但是,当我noImplicitAny启用标志时出现类型错误(由于项目范围的配置,我无法关闭该标志),因为 TypeScript 然后抱怨:

元素隐式具有“任何”类型,因为类型“CssStyleObject”没有索引签名。

我在这里看到了一个建议的解决方案:在启用 noImplicitAny 标志编译打字稿时,如何防止错误“对象类型的索引签名隐含地具有'任何'类型”?any,但如果有其他可行的解决方案,我希望避免简单地转换为。

有趣的是,一旦我在分配给style变量时使用自定义属性名称,错误消息就会消失:

const style: CssStyleObject = {
    '--foobar': '50%'
};
style.backgroundColor = 'red';
style['--my-custom-css-property'] = '16px';

请参阅上面 TypeScript Playground 上的代码

我怀疑这是因为 for 的类型style随后以某种方式被修改为简单Dictionary<string>而不是 union 类型,因此不会引发进一步的错误。


更新和解决方案

所以,事实证明,我天生就混淆了 TypeScript 中的交集联合类型。在这种情况下,是CssStyleObject的交集,所以这就足够了:Partial<CSSStyleDeclaration> Dictionary<Nullable<string>>

type Dictionary<T> = { [key: string]: T };
type Nullable<T> = T | null;

// Use `&` for creating an intersection type!
type CssStyleObject =
  Partial<CSSStyleDeclaration> &
  Dictionary<Nullable<string>>;

标签: typescriptvue.js

解决方案


首先,您的代码的当前行为是因为区分联合类型的工作方式。CSSStyleDeclarationDictionary 没有索引签名。因此,当您在声明中添加自定义属性时,类型被推断为Dictionary.

其次,如果要向现有类型添加索引,则不应使用联合类型。您应该改用交集类型。也就是说,CssStyleObject是一个CSSStyleDeclaration并且有一个索引签名,但不是一个要么是要么CssStyleObject有索引签名的对象

最后,这是工作代码

type CssStyleObject = Partial<CSSStyleDeclaration> & Record<string, string | null>
// or 
interface CssStyleObject extends Partial<CSSStyleDeclaration> {
    [key: string]: string | null
}

推荐阅读