如何创建元组的并集?

huangapple go评论94阅读模式
英文:

How to create union of tuples?

问题

我正在开发一个名为Pure Terminal的库,它是用TypeScript重写的jQuery Terminal。

我有以下代码:

  1. type Effect = 'g' | 'b' | 'i' | 'u' | 's' | 'o';
  2. type NotEffect = `-${Effect}`;
  3. type Item = '!' | '@';
  4. type Effects = Array<Effect | NotEffect | Item>;
  5. type Stack = Array<string>;
  6. type Style = {[key: string]: string};
  7. type Attrs = {[key: string]: string} & {style?: Style};
  8. type Formating = [
  9. Effects,
  10. string,
  11. string,
  12. Array<string> | undefined,
  13. string | undefined,
  14. Attrs | undefined
  15. ];
  16. const output: Formating = [[], '', '', undefined, undefined, undefined];

在格式化中添加undefined是为了解决具有不同长度元素的联合类型的问题,这导致了以下问题:

  1. type Formating = [Effects, string, string]
  2. | [Effects, string, string, Array<string>]
  3. | [Effects, string, string, Array<string>, string]
  4. | [Effects, string, string, Array<string>, string, Attrs]

当我想设置格式化的第4个及更多元素时,添加此类型会导致错误。添加undefined解决了TypeScript的问题,但它改变了JavaScript代码的行为。我需要添加一些hack来解决格式化中存在undefined值的问题。

有没有办法使Formating成为一个具有可变数量元素的元组?

编辑

这是我的真实代码:

  1. const class_i = 3; // formatting中类的索引
  2. const attrs_i = 5; // formatting中属性的索引
  3. function get_inherit_style(stack: Stack) {
  4. const output: Formating = [[], '', ''];
  5. function update_attrs(value: string) {
  6. if (!output[attrs_i]) {
  7. output[attrs_i] = {};
  8. }
  9. try {
  10. const new_attrs = JSON.parse(value);
  11. if (new_attrs.style) {
  12. const new_style = new_attrs.style;
  13. const old_style = output[attrs_i].style;
  14. output[attrs_i] = {
  15. ...new_attrs,
  16. ...output[attrs_i],
  17. ...{
  18. style: update_style(new_style, old_style)
  19. }
  20. };
  21. } else {
  22. output[attrs_i] = {
  23. ...new_attrs,
  24. ...output[attrs_i]
  25. };
  26. }
  27. } catch (e) {
  28. warn('Invalid JSON ' + value);
  29. }
  30. }
  31. if (!stack.length) {
  32. return output;
  33. }
  34. for (let i = stack.length; i--;) {
  35. let formatting = parse_formatting(stack[i]);
  36. if (formatting.length > 5) {
  37. const last = formatting.slice(5).join(';');
  38. formatting = formatting.slice(0, 5).concat(last);
  39. }
  40. const style = formatting[0].split(/(-?[@!gbiuso])/g).filter(Boolean);
  41. style.forEach(function(s) {
  42. if (is_valid_effect(s) && output[0].indexOf(s) === -1) {
  43. output[0].push(s);
  44. }
  45. });
  46. for (let j = 1; j < formatting.length; ++j) {
  47. const value = formatting[j].trim();
  48. if (value) {
  49. if (j === class_i) {
  50. if (!output[class_i]) {
  51. output[class_i] = [];
  52. }
  53. const classes = value.split(/\s+/);
  54. output[class_i] = output[class_i].concat(classes);
  55. } else if (j === attrs_i) {
  56. update_attrs(value);
  57. } else if (!output[j]) {
  58. output[j] = value;
  59. }
  60. }
  61. }
  62. }
  63. return stringify_formatting(output);
  64. }

我遇到了一个错误:

元组类型'[Effects, string, string]'的长度为'3',在索引'3'处没有元素

当访问output[class_i]时。

英文:

I'm working on a library Pure Terminal which is rewriting of jQuery Terminal in TypeScript.

I have this code:

  1. type Effect = &#39;g&#39; | &#39;b&#39; | &#39;i&#39; | &#39;u&#39; | &#39;s&#39; | &#39;o&#39;;
  2. type NotEffect = `-${Effect}`;
  3. type Item = &#39;!&#39; | &#39;@&#39;;
  4. type Effects = Array&lt;Effect | NotEffect | Item&gt;;
  5. type Stack = Array&lt;string&gt;;
  6. type Style = {[key: string]: string};
  7. type Attrs = {[key: string]: string} &amp; {style?: Style};
  8. type Formating = [
  9. Effects,
  10. string,
  11. string,
  12. Array&lt;string&gt; | undefined,
  13. string | undefined,
  14. Attrs | undefined
  15. ];
  16. const output: Formating = [[], &#39;&#39;, &#39;&#39;, undefined, undefined, undefined];

Adding undefined to formatting was a workaround to fix the issue with Union types with different length of the elements, that was causing the issue:

  1. type Formating = [Effects, string, string]
  2. | [Effects, string, string, Array&lt;string&gt;]
  3. | [Effects, string, string, Array&lt;string&gt;, string]
  4. | [Effects, string, string, Array&lt;string&gt;, string, Attrs]

Adding this type gives errors when I want to set 4th and next elements of the formatting. Adding undefined solved the issue with TypeScript, but it change the behavior of JavaScript code. I need to add hacks to fix the issue that formatting has undefined values.

Is there a way to make Formating a tuple with a variable number of elements?

EDIT

This is my real code:

  1. const class_i = 3; // index of the class in formatting
  2. const attrs_i = 5; // index of attributes in formattings
  3. function get_inherit_style(stack: Stack) {
  4. const output: Formating = [[], &#39;&#39;, &#39;&#39;];
  5. function update_attrs(value: string) {
  6. if (!output[attrs_i]) {
  7. output[attrs_i] = {};
  8. }
  9. try {
  10. const new_attrs = JSON.parse(value);
  11. if (new_attrs.style) {
  12. const new_style = new_attrs.style;
  13. const old_style = output[attrs_i].style;
  14. output[attrs_i] = {
  15. ...new_attrs,
  16. ...output[attrs_i],
  17. ...{
  18. style: update_style(new_style, old_style)
  19. }
  20. };
  21. } else {
  22. output[attrs_i] = {
  23. ...new_attrs,
  24. ...output[attrs_i]
  25. };
  26. }
  27. } catch (e) {
  28. warn(&#39;Invalid JSON &#39; + value);
  29. }
  30. }
  31. if (!stack.length) {
  32. return output;
  33. }
  34. for (let i = stack.length; i--;) {
  35. let formatting = parse_formatting(stack[i]);
  36. if (formatting.length &gt; 5) {
  37. const last = formatting.slice(5).join(&#39;;&#39;);
  38. formatting = formatting.slice(0, 5).concat(last);
  39. }
  40. const style = formatting[0].split(/(-?[@!gbiuso])/g).filter(Boolean);
  41. style.forEach(function(s) {
  42. if (is_valid_effect(s) &amp;&amp; output[0].indexOf(s) === -1) {
  43. output[0].push(s);
  44. }
  45. });
  46. for (let j = 1; j &lt; formatting.length; ++j) {
  47. const value = formatting[j].trim();
  48. if (value) {
  49. if (j === class_i) {
  50. if (!output[class_i]) {
  51. output[class_i] = [];
  52. }
  53. const classes = value.split(/\s+/);
  54. output[class_i] = output[class_i].concat(classes);
  55. } else if (j === attrs_i) {
  56. update_attrs(value);
  57. } else if (!output[j]) {
  58. output[j] = value;
  59. }
  60. }
  61. }
  62. }
  63. return stringify_formatting(output);
  64. }

I've got an error:

> Tuple type '[Effects, string, string]' of length '3' has no element at index '3'

When Accessing output[class_i]

答案1

得分: 2

TypeScript支持元组类型中的可选元素,可以通过在类型的末尾添加?来表示,如果使用未标记的元组元素:

  1. type Formatting = [Effects, string, string, Array<string>?, string?, Attrs?];

如果使用带标签的元组元素,则将?添加到标签的末尾:

  1. type Formatting = [a: Effects, b: string, c: string, d?: Array<string>, e?: string, f?: Attrs];

无论哪种方式,都可以允许相应的元素存在或缺失:

  1. const output: Formatting = [[], '', '']; // 正确
  2. const output2: Formatting = [[], '', '', ["a"], "abc"]; // 正确

代码的 Playground 链接

英文:

TypeScript supports optional elements in tuple types which are written by adding ? to either the end of the type if using unlabeled tuple elements:

  1. type Formatting =
  2. [Effects, string, string, Array&lt;string&gt;?, string?, Attrs?];

or to the end of the label if using labeled tuple elements:

  1. type Formatting =
  2. [a: Effects, b: string, c: string, d?: Array&lt;string&gt;, e?: string, f?: Attrs];

Either way will have the effect of allowing the corresponding elements to be present or missing:

  1. const output: Formatting = [[], &#39;&#39;, &#39;&#39;]; // okay
  2. const output2: Formatting = [[], &#39;&#39;, &#39;&#39;, [&quot;a&quot;], &quot;abc&quot;]; // okay

Playground link to code

huangapple
  • 本文由 发表于 2023年8月9日 04:34:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/76863045.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定