澄清
任何人都知道什么是'string | 字符串[]' 类型?我的意思是,如果我想在打字稿中使用两个字符串的逻辑“或”。怎么做?
string | string[]
是一个联合类型(TS Docs),这意味着相对值可以是string
OR string[]
(或Array<string>
字符串数组)。
当且仅当左操作数包含错误类型(、、和)时,两个变量之间的逻辑或运算符||
实际上会产生两个变量类型的并集,否则会产生第一个变量类型。虚假类型实际上取决于配置(请参阅下面的实际解决方案注释)。例子:undefined
null
number
string
boolean
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[]' 类型转字符串类型?
“强制转换”(正确的名称是类型断言(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.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的回答所述,属于 type 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
else if
if (!token) ...
实际上,有一些使用扩展令牌(逗号分隔的令牌)的身份验证技术,但在安全实现的日常使用中非常稀缺。
另请注意,理论上客户端不应发送多个具有相同名称的标头,但实际上恶意用户可以使用重复标头模拟对您的服务器的调用,以利用您应用程序的某些漏洞。这就是为什么您还应该验证数组案例的原因。