用打字稿动态调用粉笔

Dynamic call of chalk with typescript

我正在使用 typescript 并想动态调用 chalk 方法,请参阅我的代码:

import chalk from 'chalk';

const color: string = "red";
const message: string = "My Title";
const light: boolean = false;

const colorName = `bg${capitalize(color)}${light ? 'Bright' : ''}`;
console.log(chalk[colorName](message));

因此,颜色函数将消息背景的颜色作为一个值。消息是内容,灯光是布尔值,用于判断背景是否应该使用亮色版本。
问题是打字稿抛出这个错误:

Element implicitly has an 'any' type because expression of type 'any' can't be used to index type 'ChalkInstance'.

chalk[colorName] 部分。

我已经试过 (chalk[colorName] as keyof ChalkInstance)。并且已经看过 (这基本上是同一个问题)。显然,回答的解决方案不适用于我的情况。

您需要做一些事情。

  1. 从变量 colormessagelight 中删除多余的显式类型。这样做不仅没有必要,而且实际上会干扰 TypeScript properly inferring the literal value of those types。换句话说,如果你定义 const color = "red"; TypeScript 会知道,因为它是一个 const,它永远不会改变,它总是可以被视为字符串文字 "red" 而不是更多通用 string。 (作为一般规则,你永远不应该显式定义 const 变量的类型。
const color = "red";
const message = "My Title";
const light = false;
  1. 确保您的 capitalize() 函数正确定义了它的 return 类型。在这种情况下,实际上有一个awesome built-in utility type Capitalize<> which you can use here。结合 TypeScript 泛型,您可以以 TypeScript 知道的方式定义函数,例如如果 "red" 是输入,"Red" 是输出。
function capitalize<S extends string>(c: S) {
  return c.replace(/\b\w/g, firstLetter => firstLetter.toUpperCase()) as Capitalize<S>;
}
  1. Use the as const assertion 当您定义 colorName(以及您可能需要定义的任何其他类似变量)时。如果不这样做,colorName 的类型将被推断为 string,这不利于索引 chalk。使用 as const,您基本上是在告诉 TypeScript 将结果表达式视为 文字字符串值 。在这种情况下,colorName 的类型变为 "bgRedBright" | "bgRed",它们都是 chalk.
  2. 的有效索引
const colorName = `bg${capitalize(color)}${light ? 'Bright' : ''}` as const;
                                                                   ^^^^^^^^

When you put it all together:

import chalk from 'chalk';

const color = "red";
const message = "My Title";
const light = false;

function capitalize<S extends string>(c: S) {
  return c.replace(/\b\w/g, firstLetter => firstLetter.toUpperCase()) as Capitalize<S>;
}

const colorName = `bg${capitalize(color)}${light ? 'Bright' : ''}` as const;

console.log(chalk[colorName](message));

编辑:将来您可能需要以动态方式定义 color 变量,而不仅仅是硬编码文字 "red" 值。在这种情况下,您需要一种方法来满足 TypeScript color 总是 是有效的东西,因为我们刚刚设置的所有其他推理。

在这里,我们实际上确实想要为某些变量显式定义类型,特别是let和函数参数。幸运的是,chalk provides 是这种情况下非常有用的类型,ForegroundColor 基本上是所有有效的“基本”颜色,并且在放入时恰好与 BackgroundColor 兼容形式 `bg${ForegroundColor`,这正是我们在这里需要的。

import chalk, { ForegroundColor } from 'chalk';

let color: ForegroundColor = "red";
color = "blue";

我们甚至可以通过更严格地控​​制参数的类型来改进 capitalize() 函数:

function capitalize<S extends ForegroundColor>(c: S) {
  return c.replace(/\b\w/g, firstLetter => firstLetter.toUpperCase()) as Capitalize<S>;
}

Another playground example that puts those further improvements into practice.

我自己找到了问题的答案。问题是 colorName 变量的类型是 any,chalk 无法将其识别为 chalk class 的索引。这里 colorName 变量的值可以包含背景颜色的所有值,无论它们是否明亮。
然后我有两个解决方案:

  • 对颜色的所有值进行硬编码(这会花费很长时间)
  • 找到表示颜色的类型并显式设置为colorName变量的类型

我的解决方案与第二个想法很接近,但是,Chalk 库没有提供颜色类型。但是,我们可以导入所有 BackgroundColors 的显式列表。然后,我们只需要将这些关键词的类型指定为colorName变量的类型即可。

import { BackgroundColor } from 'chalk';

import chalk from 'chalk';

const color: string = "red";
const message: string = "My Title";
const light: boolean = false;

const colorName = `bg${capitalize(color)}${light ? 'Bright' : ''}` as typeof BackgroundColor;
console.log(chalk[colorName](message));

PS:注意变量colormessagelight的类型是hard-coded。在应用程序上,这些值是动态的,这是一种更清晰的方式。