【问题标题】:Typescripts type 'string | string[]' is not assignable to type 'string', what is the 'string | string[]' type? how to cast them to string?打字稿类型'字符串| string[]' 不能分配给类型'string',什么是'string |字符串[]' 类型?如何将它们转换为字符串?
【发布时间】:2019-09-20 14:16:53
【问题描述】:

当我使用 TypeScript 时:

let token = req.headers['x-access-token'] || req.headers['authorization'] as string;

我有其他错误:

Argument of type 'string | string[]' is not assignable to parameter of type 'string'

任何人都知道什么是'string |字符串[]' 类型?我的意思是如果我想在打字稿中使用两个字符串的逻辑“或”。怎么做?

以及如何转换'string | string[]' 类型转字符串类型?

【问题讨论】:

    标签: string typescript logical-or


    【解决方案1】:

    试试

    let token = (req.headers['x-access-token'] || req.headers['authorization']) as string;
    

    编译器认为 req.headers['some string'] 是一个字符串数组,当你转换 or 运算符的一侧时,你会得到一个字符串类型或字符串数​​组。所以对它们都执行 or ,然后将结果强制为字符串。

    【讨论】:

      【解决方案2】:

      我猜你正在使用 node.js。在这种情况下,req.headers 的类型为 IncomingHttpHeaders,其索引签名为:[header: string]: string | string[] | undefined;
      这意味着,req.headers['whatever'] 的类型可以是 string 或 string[](字符串数组)或 undefined

      • 逻辑或req.headers['x-access-token'] 的第一部分具有string | string[] | undefined 类型
      • 由于强制转换表达式,第二部分req.headers['authorization'] as string 的类型为string
      • token 的类型是string | string[],因为
        • 定义第一部分时,可以是string | string[]
        • 当第一部分未定义时,or 将使用类型为string 的第二部分

      提示
      你可以使用 req.headers.authorization 而不是 req.headers['authorization'],它的类型是 string | undefined。

      interface IncomingHttpHeaders {
          .. 
          'authorization'?: string;
          ..
          [header: string]: string | string[] | undefined;
      }
      

      详情
      注意:Adrian Brand 的答案很好,你可以按原样使用。为了完整起见,我将详细说明如何处理所有情况并解释类型:

      const tokenValue= req.headers['x-access-token'] || req.headers['authorization'];
      

      tokenValue 的类型为 string | string[] | undefined。
      请注意,当不存在任何标头时,它也可以是 undefined。
      我们可以处理这种情况:

      if (!tokenValue) throw Error('missing header')
      

      在此检查之后,打字稿足够聪明,可以知道 tokenValue 现在是 string | string[] 类型

      if (Array.isArray(tokenValue)) {
        // In this if branch the type of `tokenValue` is `string[]`
      } else {
        // In this if branch, the type of `tokenValue` is `string`
      }
      

      【讨论】:

      • @Ernesto 将“用户代理”转换为字符串是错误的。 IncomingHttpHeaders['user-agent']的类型是string | undefined,根据RFC723,发送user-agent是可选的。因此,您应该使用默认值,而不是强制转换为字符串:const userAgent = req.headers["user-agent"] || '';
      • @Ernesto 问题与“用户代理”无关。 OP 还希望获得 x-access-token 的值(未在 IncomingHttpHeaders 中显式键入,因此类型为 string | string[] | undefined)。即在运行时你可以得到一个字符串数组。并且将数组转换为字符串很可能会导致运行时错误,因此最好明确处理这种情况。
      • @Ernesto 我猜,你没有看问题中的代码示例:req.headers['x-access-token'] || req.headers['authorization']
      • @Ernesto:这是一个简短的示例,可以帮助您理解为什么将string[] 转换为string 是一个坏主意(单击Run 按钮):typescript playground
      • @Ernesto 在这种情况下你是不正确的。我强烈建议不要把这变成一场关于自我的斗争,等等,你去对这个话题做更多的研究。在这种情况下,将数组转换为字符串根本不会帮助用户,因为基础类型可以是数组、字符串或未定义。
      【解决方案3】:

      这是因为可能有多个相同的标题。

      在这里,我返回了标头,或者如果它是一个数组,则返回该标头的第一个实例。

      const getHeader = (name) => Array.isArray(req.headers[name]) ? req.headers[name][0] : req.headers[name];
      let token = getHeader('x-access-token') ?? getHeader('authorization');
      

      【讨论】:

      • 很高兴看到这被否决了,但当前是唯一正确的答案。
      【解决方案4】:

      说明

      任何人都知道什么是'string |字符串[]' 类型?我的意思是如果我想在打字稿中使用两个字符串的逻辑“或”。怎么做?

      string | string[] 是 type union (TS Docs),这意味着相对值可以是 string OR string[](或 Array<string> 或字符串数​​组)。

      两个变量之间的逻辑或运算符|| 实际上会产生两个变量类型的并集当且仅当左操作数包含 falsish 类型(undefined、null、@ 987654332@、string 和 boolean),否则生成第一个变量类型。 falsish 类型实际上取决于配置(请参阅下面的实际解决方案注释)。示例:

      type NonFalsishType = { prop: number };
      let var1: number | undefined = 42;
      let var2: string = 'var2'
      let var3: NonFalsishType  = { prop: 42 };
      
      let test1: number | string = var1 || var2;
      let test2: number | string = var2 || var1;
      let test3: string | NonFalsishType = var2 || var3;
      
      // strictNullCheck = true
      // string type can be omitted because NonFalsishType
      // is defenitely not falsy
      let test4: NonFalsishType = var3 || var2;
      
      // strictNullCheck = false
      // string not omitted because null can be assigned to var3
      let test4: NonFalsishType | string/* | null*/ = var3 || var2;
      

      以及如何转换'string | string[]' 类型转字符串类型?

      “强制转换”(正确的名称是type assertion (TS Docs),它是一个语义不同的概念)可以通过不同的方式完成,最常见的是使用as关键字来实现,但也有尖括号符号:

      // as
      let myHeader: string = req.headers['authorization'] as string
      
      // angle brackets
      let myHeader: string = <string>req.headers['authorization']
      

      注意:类型断言在运行时根本不做任何事情,它也不会在 JS 代码中编译:

      // TS
      let myHeader: string = req.headers['authorization'] as string
      
      // JS
      var myHeader = req.headers['authorization'];
      

      类型断言只是为了指示 TS 类型检查器强制将一个类型限制为另一个类型,仅在编译阶段的类型检查期间。这就像对编译器说“我不在乎变量实际上是哪种(联合)类型,对待它as它将是这个指定的类型” em>

      可能的解决方案

      现在最简单的解决方案是为您的变量声明 string 类型:

      // parenthesis emphasize where the assertion is applied
      
      let token: string = (req.headers['x-access-token'] as string) ||
                          (req.headers['authorization'] as string);
      
      let token: string = (
          req.headers['x-access-token'] ||
          req.headers['authorization']
      ) as string;
      
      // no runtime differences, just saying to the TS type checker
      // two different way to see the same variables
      

      这种解决方案会导致不同的问题:如果客户端向服务器发送多个x-access-token/authorization 标头怎么办?

      您最终会在 token 变量中得到一个数组,这意味着您生成的代码可能会中断(例如,token.substr(10) 将产生运行时错误,因为数组没有字符串具有的substr 属性)。

      如果客户端根本不发送x-access-token/authorization 标头(undefined 属性将中断任何访问器的执行),那就更糟了。

      真正的解决方案

      你需要考虑你需要实现什么。 TypeScript 类型表示法不仅用于修饰您的代码,而且实际上是通过类型和模式检查生成高质量的代码。不要忽视变量可以有多种类型的事实,否则在生产环境中会出现错误和安全问题。

      如果您的真正意图是验证访问令牌,则应确保令牌非空并且是唯一的,以便识别用户:

      // usually is a bad practice to authorize with multiple headers
      // but it does not produce significant runtime error doing this
      let token: string | string[] | undefined = req.headers['x-access-token'] || req.headers['authorization'];
      
      if (typeof(token) === 'undefined') {
          // no token passed in the header
          // token variable is of type 'undefined' in this scope
         
          // probably here, if the page require access, you should
          // respond with a 401 unauth code
      
          // you can skip this check by appending a check at 
          // the end of token variable initialization like this:
          // let token: string | string[] = ... || '';
      }
      else if (Array.isArray(token)) {
          // multiple tokens passed in the header
          // token variable is of type 'string[]' in this scope
         
          // for this special case see multiple tokens notes (at the end)
      }
      else if (!token) {
          // the header contains the token but is actually an empty string
          // token variable is of type 'string' in this scope
      
          // same as undefined token, if the page require access, you should
          // respond with a 401 unauth code
      }
      else {
          // the header contains a non-empty string token
          // token variable is of type 'string' also in this scope
      
          // validate your token and respond by consequence (200 OK / 401 unath)
      }
      

      注意:

      req.headers[key],如@TmTron 的回答所述,属于string | string[] | undefined 类型,但错误中的联合类型中未提及undefined。这是因为可以配置 TypeScript(在tsconfig.json 中或通过传递正确的命令行参数)在类型检查期间忽略虚假值(如false、null 和undefined)。选项是strictNullCheck (TS Docs),默认设置为false(意味着TS在类型检查时会忽略虚假值)。如果您将该选项设置为true,则错误将变为:

      Argument of type 'string | string[] | undefined' is not assignable to parameter of type 'string'
      

      迫使您还考虑undefined 案例(根据我的经验,这通常可以防止很多非常多的错误)

      多代币备注

      多个token的情况比较模糊,应该取一个约定 用你的意图:

      • 始终拒绝多个令牌 - 最佳、建议和常见做法 (401 unath)
      • 如果多个令牌引用不同的用户,则拒绝多个令牌 - 如果有可能,例如忽略并丢弃过期令牌,但检查它们是否引用同一用户(验证令牌 - 200 OK / 401 unath)
      • 仅接受第一个令牌为有效:只需使用token = token[0] || '' 并在随后的else if 中删除else(变为if (!token) ...) - 仍然可行,但不是真正干净的解决方案

      实际上有一些使用扩展令牌(逗号分隔令牌)的身份验证技术,但在日常安全实现的使用中非常稀缺。

      另外请注意,理论上客户端不应发送多个同名的标头,但实际上恶意用户可能会使用重复的标头模拟对您的服务器的调用,以利用您应用程序的某些漏洞。这就是为什么您还应该验证数组大小写的原因。

      【讨论】:

        猜你喜欢
        • 2021-09-21
        • 2022-01-17
        • 2019-04-10
        • 2021-06-12
        • 2021-08-19
        • 2019-10-30
        • 2021-09-09
        • 2021-07-02
        • 1970-01-01
        相关资源
        最近更新 更多