Typescript 中变量的动态字符串文字类型

Sam*_*mmy 2 string-literals typescript

我从身份验证提供商处获取带有声明的令牌。它是一个 JSON 对象,如下所示:

{
  "email": "user@example.org",
  "urn:dev:claims/user-id": "123"
}
Run Code Online (Sandbox Code Playgroud)

我正在尝试在客户端上创建一个代表性界面或类型以正确访问此令牌密钥。问题是上面“urn:dev:claims/user-id”的“dev”部分是动态的,它来自环境变量,如下所示:

const claimKey = urn:${process.env.REACT_APP_ENV}:claims/user-id

当我尝试以下操作时:

interface MyInterface {
  email: string;
  [claimKey]: string;
}
Run Code Online (Sandbox Code Playgroud)

这是行不通的。

这是一个完整的可重现示例:

{
  "email": "user@example.org",
  "urn:dev:claims/user-id": "123"
}
Run Code Online (Sandbox Code Playgroud)

jca*_*alz 5

TypeScript 目前无法完全满足您的要求:

  • 您希望编译器将其视为process.env.REACT_APP_ENV某些“未知但唯一”string的文字,就像unique symbol处理symbol- 类型值一样。microsoft/TypeScript#33038上有一个实验性拉取请求,该请求允许诸如 之类的事情unique string,但它从未进入该语言。

  • 此外,您需要能够将该唯一字符串连接到其他字符串文字并获得某种唯一输出;也许这还需要支持unique string内部“模式”模板文字类型,如microsoft/TypeScript#40598中实现的那样,并且这是否有效并不明显。

  • 即使这一切都已解决,您目前也不能使用模式模板文字类型作为对象键;请参阅microsoft/TypeScript#42192。不幸的是,像这样的对象类型Record<`foo${string}`, number>被非常类似地对待{}{fooOops: "This is not a number"}如果你给它分配一个类似的类型,它不会抱怨。(好吧,至少这部分对于 TS4.4 是固定的;模式模板文字可以按照microsoft/TypeScript#44512在索引签名中使用)

所有这些加在一起意味着从 TS 4.4 开始,这不属于 TypeScript 的可能性范围。


相反,您需要某种解决方法。我正在尝试使用字符串enum来模拟与键一起使用的不透明/名义子类型string,但它实际上并没有比我选择的解决方法更有用:我们假装"###${process.env.REACT_APP_ENV}###"已知的占位符字符串文字类型的实际类型process.env.REACT_APP_ENV。只要我们只将类型称为process.env.REACT_APP_ENV而不是假装字符串文字,一切都会成功。我们甚至可能希望假装值类似于"!!!PLACEHOLDER_DO_NOT_USE_THIS!!!"或任何您需要说服人们不要使用文字类型的值。

它看起来像这样:

declare const process: {
  env: {// the following type is a fiction but is the best I can do 
    REACT_APP_ENV: "###${process.env.REACT_APP_ENV}###"
  }
}
Run Code Online (Sandbox Code Playgroud)

然后你claimKey将是一个const断言模板字符串,以便编译器可以连接它并维护它的字符串文字:

const claimKey = `urn:${process.env.REACT_APP_ENV}:claims/user-id` as const
Run Code Online (Sandbox Code Playgroud)

一切都按预期进行,主要是:

interface MyInterface {
  email: string;
  [claimKey]: string;
}

const myInterface: MyInterface = {
  email: "user@example.org",
  [claimKey]: "123"
}
Run Code Online (Sandbox Code Playgroud)

万岁!不过,这只是一个解决方法。不幸的是,该占位符值可能会显示为 IntelliSense 提示:

const booIntelliSense: MyInterface = {
  email: "",
  "urn:###${process.env.REACT_APP_ENV}###:claims/user-id": "oops" // <-- hinted by IntelliSense!
}
Run Code Online (Sandbox Code Playgroud)

所以它确实不完美。那好吧。

Playground 代码链接