创建递归样式对象时出现打字稿问题

时间:2019-11-11 16:28:43

标签: typescript

我需要制作一个递归样式对象,该对象可以支持样式属性并采用嵌套格式。

我很难解决这个问题,并且我已经尝试了所有可以在SO和google上找到的解决方案。

interface Properties {
  border?: string;
  width?: string;
}

//# 1 Type attempt
type TRecursiveProperties = Properties & {
  [index: string]: TRecursiveProperties;
};

//# 2 Interface attempt
interface IRecursiveProperties extends Properties {
  [index: string]: IRecursiveProperties;
}


const test: TRecursiveProperties = {
  border: '1px solid green',
  isActive: {
    border: '2px solid red',
    '&:hover': {
      border: '3px solid blue'
    }
  }
};

我希望递归属性是一种回退/捕获所有或某种方式来从Properties对象中排除键的方法。

我遇到的两个错误都是

  

类型别名'TRecursiveProperties'循环引用自身。

     

类型'string'的属性'width'不能分配给字符串索引类型'IRecursiveProperties'

有什么想法可以实现这一目标吗?

2 个答案:

答案 0 :(得分:1)

我会这样做:

interface Properties {
  width?: string;
  border?: string;
  [selector: string]: string | Properties | undefined;
}

const myStyles: Properties = {
  'width': '100px',
  ' .child': {
    'width': '200px',
    'border': '1px color blue',
    '&:hover': {
      border: '1px solid aquamarine',
    },
  },
};

在此打字稿资源:https://basarat.gitbooks.io/typescript/docs/types/index-signatures.html中,搜索“设计模式:嵌套索引签名”以查看非常相似的示例。

答案 1 :(得分:1)

没有一种具体的类型可以准确地表示您要执行的操作:即“特殊情况”某些属性并将其从索引签名中排除。它已经requested,但是到目前为止还没有办法。

请注意,我说过没有具体类型。您可以将其表示为将值约束到的泛型类型。因此,您不必拥有类型RecursiveProperties的值,而拥有类型T extends VerifyRecursiveProperties<T>的值。像这样:

type VerifyRecursiveProperties<T> = Properties & { [K in Exclude<keyof T, keyof Properties>]:
  T[K] extends object ? VerifyRecursiveProperties<T[K]> : never }

然后您需要一个辅助函数来推断特定的T,而不必自己写出来:

const asRecursiveProperties = <T extends VerifyRecursiveProperties<T>>(t: T) => t;

这使您可以执行想要的操作:

const test = asRecursiveProperties({
  border: '1px solid green',
  isActive: {
    border: '2px solid red',
    '&:hover': {
      border: '3px solid blue'
    }
  }
}); // okay

,如果违反约束,还会给出错误:

asRecursiveProperties({
  border: 1 // error, number is not string
})

asRecursiveProperties({
  isActive: "" // error, string is not never
})

asRecursiveProperties({
  foo: {
    bar: {
      baz: {
        border: 1, // error, number is not string
      }
    }
  }
})

如果这太复杂了,您可能想要放松约束以允许索引签名接受string | undefined(如the other answer),或者重构您的类型,以免您尝试将您的Properties属性推入与递归属性相同的对象中,如下所示:

interface RefactoredRecursiveProperties extends Properties {
  nested?: { [k: string]: RefactoredRecursiveProperties }
}

const test2: RefactoredRecursiveProperties = {
  border: '1px solid green',
  nested: {
    isActive: {
      border: '2px solid red',
      nested: {
        '&:hover': {
          border: '3px solid blue'
        }
      }
    }
  }
}

这种重构对您来说可能不是理想的选择,但是对于编译器来说,重构要简单得多。


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

Link to code