typescript 如何允许使用无效密钥?

mnemlml8  于 2022-11-26  发布在  TypeScript
关注(0)|答案(2)|浏览(134)

我有张Map:

const dataMap: {
    KEY01: { label: 'some string', details: {....}},
    KEY02: { label: 'some other string', details: {...}},
}

const copy = {
 title: dataMap[keyVariable].label,
 info: dataMap[keyVariable].details,
}

因此keyVariable来自第三方包(有自己的类型)。有更多可能的键'KEY 03','KEY 04',...,等等。
现在我100%认为在我的应用程序中只有KEY 01和KEY 02是可能的。问题是TS抱怨其他可能的keyVariable选项没有包括在我的dataMap中。

Property 'KEY03' does not exist on type { here insert the shape of my dataMap }

我宁愿不执行ts-ignore或ts-expect-error。(KEY 03,KEY 05)添加到我的dataMap中,但这毫无意义,而且会包含大量不必要的代码。(!-但我不知道该把它放在哪里)告诉TS,我知道keyVariable可以有更多的值(除了'KEY 01'和'KEY 02'),但我知道它们在此上下文中是不可能的?

az31mfrm

az31mfrm1#

这是您目前所在的位置:
TSPlayground

declare const keyVariable: 'KEY01' | 'KEY02' | 'KEY03' | 'KEY04' /* etc. */;

const dataMap = {
  KEY01: { label: 'some string', details: {someKey: 'some value'}},
  KEY02: { label: 'some other string', details: {someKey: 'some value'}},
};

const copy = {
  title: dataMap[keyVariable].label, /*
                 ~~~~~~~~~~~
  Property 'KEY03' does not exist on type... */
  info: dataMap[keyVariable].details, /*
                ~~~~~~~~~~~
  Property 'KEY03' does not exist on type... */
};

为了修复编译器诊断错误,可以采用多种方法之一。
最具类型安全性的方法(包括在运行时)是实际验证并确保keyVariable值是您期望的值之一。您可以使用类型保护函数来实现这一点:
TSPlayground

declare const keyVariable: 'KEY01' | 'KEY02' | 'KEY03' | 'KEY04' /* etc. */;

const dataMap = {
  KEY01: { label: 'some string', details: {someKey: 'some value'}},
  KEY02: { label: 'some other string', details: {someKey: 'some value'}},
};

function isDataMapKey (value: unknown): value is keyof typeof dataMap {
  return value as keyof typeof dataMap in dataMap;
}

if (isDataMapKey(keyVariable)) {
  const copy = {
    title: dataMap[keyVariable].label, // OK
                 //^? const keyVariable: "KEY01" | "KEY02"
    info: dataMap[keyVariable].details, // OK
                //^? const keyVariable: "KEY01" | "KEY02"
  };
}
else {
  // Handle the case that your assumption is wrong, for example:
  throw new Error('Oops, I was wrong');
}

不太安全的方法是使用类型Assert。这不是类型安全的,但却是一种告诉编译器“嘘......我知道的比你知道的多--相信我”的方法。这没有运行时开销,但如果你的假设被证明是错误的,则会在运行时以不正确为代价--在这种情况下,你会有一个实际的运行时bug。这就是你可以使用类型Assert的方法:
TSPlayground

declare const keyVariable: 'KEY01' | 'KEY02' | 'KEY03' | 'KEY04' /* etc. */;

const dataMap = {
  KEY01: { label: 'some string', details: {someKey: 'some value'}},
  KEY02: { label: 'some other string', details: {someKey: 'some value'}},
};

const copy = {
  title: dataMap[keyVariable as keyof typeof dataMap].label, // OK
  info: dataMap[keyVariable as keyof typeof dataMap].details, // OK
};

在我自己的代码中,我选择第一种方法的概率为99.9%:让我的程序多花几纳秒的时间来确保我的期望是正确的,与其他选择相比,这是一个可以忽略不计的代价。
另请参阅:Assert函数和类型系统

lymgl2op

lymgl2op2#

我同意jsejcksn所说的一切。处理它的其他几个想法:

function assertIsValidKey(key: string): asserts key is keyof typeof dataMap {
  if (!(key in dataMap)) throw new Error('Invalid key');
}

assertIsValidKey(keyVariable);
dataMap[keyVariable].label;

或者是这样的:

function castValidKey(key: string): keyof typeof dataMap {
  if (!(key in dataMap)) throw new Error('Invalid key');
  return key as keyof typeof dataMap;
}

dataMap[castValidKey(keyVariable)].label;

相关问题