您走在正确的轨道上,基本上是正确的。
接下来,我假设您正在使用
--strict
或至少
--strictNullChecks
编译器选项,以便
undefined
和
null
不总是隐式允许的。
let oops: string = undefined; // error!
// Type 'undefined' is not assignable to type 'string'
在TypeScript中,使用
?
修饰符标记为
可选的函数/方法参数或对象类型字段表示它们可以
缺失:
function opt(x?: string) { }
interface Opt {
x?: string;
}
const optObj: Opt = {};
opt();
但是这样的可选参数/字段也可以被 存在但未定义
:
const optObj2: Opt = { x: undefined } // okay
opt(undefined); // okay
实际上,如果您使用 IntelliSense 检查这些可选参数/字段的类型,您会发现编译器自动将 undefined
添加为可能性:
function opt(x?: string) { }
interface Opt {
x?: string;
}
type AlsoOpt = Pick<Opt, "x">;
从函数的实现者或对象类型的消费者的角度来看,可选元素可以被视为始终存在,但可能是未定义的:
function opt(x?: string) {
console.log(typeof x !== "undefined" ? x.toUpperCase() : "undefined");
}
function takeOpt(v: Opt) {
const x = v.x;
console.log(typeof x !== "undefined" ? x.toUpperCase() : "undefined");
}
将此与包含| undefined
的必需(非可选)字段或参数进行比较和对比:
function req(x: string | undefined) { }
interface Req {
x: string | undefined
}
与可选版本一样,带有| undefined
的必需版本接受显式的undefined
。但与可选版本不同的是,必需版本不能完全缺少调用或创建的值:
req();
req(undefined);
const reqObj: Req = {};
const reqObj2: Req = { x: undefined }
就像可选版本一样,函数的实现者或对象类型的使用者会将可选内容视为明确存在但可能为undefined
:
function req(x: string | undefined) {
console.log(typeof x !== "undefined" ? x.toUpperCase() : "undefined");
}
function takeReq(v: Req) {
const x = v.x;
console.log(typeof x !== "undefined" ? x.toUpperCase() : "undefined");
}
其他需要注意的事项:
元组类型中还有可选元素,它们的工作方式与可选对象字段相同。它们像参数一样有相同的限制:如果任何元组元素是可选的,则所有后续元素也必须是可选的:
type OptTuple = [string, number?];
const oT: OptTuple = ["a"]; // okay
const oT2: OptTuple = ["a", undefined]; // okay
type ReqTuple = [string, number | undefined];
const rT: ReqTuple = ["a"]; // error! Source has 1 element(s) but target requires 2
const rT2: ReqTuple = ["a", undefined]; // okay
对于函数参数,有时也可以使用类型
void
表示“缺失”,因此
| void
表示“可选”,这在
microsoft/TypeScript#27522中实现。因此,
x?: string
和
x: string | void
被视为类似:
function orVoid(x: string | void) {
console.log((typeof x !== "undefined" ? x.toUpperCase() : "undefined"));
}
orVoid();
这在对象字段方面尚未实现。
microsoft/TypeScript#40823已经被实现,但尚未应用于该语言中(我不确定它是否会被应用)。
interface OrVoid {
x: string | void;
}
const o: OrVoid = {}
最后,我会指向
microsoft/TypeScript#13195,这是一个讨论TypeScript中“缺失”和“存在但
undefined
”之间有趣历史关系的问题。有时它们被视为相同,而有时它们是不同的。
人们希望在某些情况下能够更加区分它们,特别是当某个值为
可选时,开发人员并不总是希望显式传入
undefined
。也就是说,人们希望说
interface Opt {x?: string}
表示
x
要么是一个
string
,要么完全不存在。他们认为,如果您有一个类型为
Obj
的值
o
,那么只有在
"x" in o
为
false
时,
o.x === undefined
才会发生。
但这不是默认情况下TypeScript中发生的。在大多数编译器配置中,
o.x === undefined
不能让你知道属性是存在但是
undefined
还是不存在。因此,
"x" in o
无法确定
o.x
是一个
string
还是
undefined
。
在TypeScript 4.4中将发布一个新的
--exactOptionalPropertyTypes
编译器标志,当启用时,将不会将
undefined
添加到可选属性的域中,因此
o.x === undefined
意味着
x
属性不存在。但是这个编译器选项不会默认启用,而且它仅适用于属性而不是函数参数。
无论如何,我建议避免出现缺失和
undefined
之间的差异很重要的情况。
玩耍链接到代码