TypeScript 的类型 'string |string[]' 不能分配给类型"字符串",什么是"字符串 |字符串[]'类型?如何将它们转换为字符串?



当我做 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[]类型是什么?我的意思是,如果我想在 TypeScript 中使用两个字符串的逻辑"或"。怎么办?

以及如何将string | string[]转换为string类型?

尝试

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

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

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

  • 逻辑或req.headers['x-access-token']的第一部分具有类型string | string[] | undefined
  • 由于演员表表达式,第 2 部分req.headers['authorization'] as string属于string
  • token的类型是string | string[],因为
    • 定义第一部分后,可以对其进行string | string[]
    • 当第一部分未定义时,or将使用类型为string的第二部分

提示
而不是req.headers['authorization']您可以使用类型为string | undefinedreq.headers.authorization

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

详细信息
注意:阿德里安·布兰德的答案很好,您可以按原样使用。为了完成起见,我将只展示如何处理所有情况的详细方法并解释类型:

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`
}

澄清

有人知道什么是"字符串|字符串[]"类型吗?我的意思是,如果我想在打字稿中使用两个字符串的逻辑"或"。怎么办?

string | string[]是一个类型联合(TS Docs),这意味着相对值可以是stringstring[](或Array<string>字符串数组)。

两个变量之间的逻辑或运算符||实际上产生了两个变量类型的并集,当且仅当左操作数包含伪造类型(undefinednullnumberstringboolean),否则产生第一个变量类型。伪造类型实际上是依赖于配置的(请参阅下面的实际解决方案注释)。例:

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;

以及如何将"字符串|字符串[]"类型转换为字符串类型?

"强制转换"(正确的名称是类型断言(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它是这种指定类型"一样对待它">

可能的解决方案

现在最简单的解决方案是断言变量的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中或通过传递正确的命令行参数)配置为在类型检查期间忽略错误值(如falsenullundefined)。该选项strictNullCheck(TS 文档),默认情况下设置为false(这意味着 TS 在类型检查时将忽略 falsy 值)。如果将该选项设置为true则错误将变为:

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

迫使您同时考虑undefined情况(根据我的经验,这通常会防止很多很多错误)

多代币说明

多个代币的情况比较模糊,你应该达成协议 与您的意图:

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

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

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

这是因为可以有多个相同的标头。

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

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

相关内容

  • 没有找到相关文章

最新更新