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

Typescripts type 'string | string[]' is not assignable to type 'string', what is the 'string | string[]' type? how to cast them to 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[]' 类型吗?我的意思是如果我想在打字稿中使用两个字符串的逻辑 'or' 。怎么做?

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

尝试

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

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

我猜你正在使用 node.js。在这种情况下,req.headersIncomingHttpHeaders 类型,它的 index-signature 为:[header: string]: string | string[] | undefined;
这意味着 req.headers['whatever'] 可以是 stringstring[] (字符串数组)或 undefined

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

提示
您可以使用类型为 string | undefined.

req.headers.authorization 而不是 req.headers['authorization']
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.
类型 请注意,当 headers 的 none 存在时,它也可以是 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`
}

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

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

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

澄清

Any one know what is 'string | string[]' type? I mean if I want use logical 'or' of two string in typescript. How to do it?

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

当且仅当左操作数包含 falsish 类型(undefinednullnumberstringboolean),否则生成第一个变量类型。 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;

And How to cast 'string | string[]' type to string type?

“转换”(正确的名称是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 它会是此指定类型的

可能的解决方案

现在最简单的解决方案是断言变量的 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 headers 怎么办?

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

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

实解

您需要考虑您需要实现的目标。 TypeScript 类型符号不仅仅用于装饰您的代码,而且实际上可以通过类型和模式检查生成具有重要意义的高质量代码。你不应该忽视一个变量可以采用多种类型这一事实,否则你在生产环境中会出现错误和安全问题。

如果您的真正意图是验证访问令牌,您应该确保该令牌是 non-empty AND 唯一的,以便识别用户:

// 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's answer, is of type string | string[] | undefined, but undefined is not mentioned in the union type in the error. This because it is possible to configure TypeScript (in the tsconfig.json or by passing the proper command line argument) to ignore falsy values during the type checking (like false, null and undefined). The option is strictNullCheck (TS Docs) 所述,默认设置为 false(意味着 TS 将在类型检查时忽略虚假值)。如果将该选项设置为 true,则错误将变为:

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

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

多个代币备注

多个token的情况比较模糊,要约定好 用你的意图:

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

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

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