我是typescript的新手,正在尝试为接受第三个参数作为可选参数的函数实现类型检查。然后根据另一个函数实参,决定是否使用第三个实参。
我让它是可选的,但是:
- 我得到一个错误。
- 即使我没有得到错误,我想知道我是否可以根据另一个(
vehicleType
)在该参数(trailer
)上切换所需/可选标志。
下面是我为这个场合准备的一个例子:
enum VehicleType {
Car,
Pickup
}
type Vehicle = {
weight: number;
length: number;
};
type Trailer = {
weight: number;
length: number;
};
function vehicle(
vehicleType: VehicleType,
vehicle: Vehicle,
trailer?: Trailer
) {
switch (vehicleType) {
case VehicleType.Car:
return `${vehicle.length} ${vehicle.weight}`;
case VehicleType.Pickup:
return `${vehicle.length + trailer.length} ${
vehicle.weight + trailer?.weight
}`;
}
}
对于这段代码,我得到两次相同的错误:
对象可能'未定义'。对于
trailer.
对象
如果类型是Pickup
,是否有一种方法可以强制编译器要求trailer
,而不是当类型是Car
时?
我从问题中得出,如果vehicleType
是Pickup
,trailer
是必需的参数。
如果是,问题是TypeScript不知道;据它所知,vehicle(VehicleType.Pickup, someVehicle)
(没有trailer
)是完全有效的。
可以使用函数重载告诉它有效的参数的具体组合;它不能完全解决问题,但它使我们接近:
function vehicle(vehicleType: VehicleType.Car, vehicle: Vehicle): string;
function vehicle(vehicleType: VehicleType.Pickup, vehicle: Vehicle, trailer: Trailer): string;
function vehicle(vehicleType: VehicleType, vehicle: Vehicle, trailer?: Trailer): string {
switch (vehicleType) {
case VehicleType.Car:
return `${vehicle.length} ${vehicle.weight}`;
case VehicleType.Pickup:
assertNotNullish(trailer);
return `${vehicle.length + trailer.length} ${vehicle.weight + trailer.weight}`;
}
}
只有前两个是公共签名;第三个是实现签名,它只是实现签名,不会被其他代码看到。
现在的问题是,在实现代码中,它仍然抱怨trailer
可能是undefined
,即使你知道(从过载签名)它不会。有两种方法可以解决这个问题:
- 非空断言操作符,为后缀
!
- 显式断言
这是非空版本,当使用trailer
时,注意!
在.
之前:
function vehicle(vehicleType: VehicleType.Car, vehicle: Vehicle): string;
function vehicle(vehicleType: VehicleType.Pickup, vehicle: Vehicle, trailer: Trailer): string;
function vehicle(vehicleType: VehicleType, vehicle: Vehicle, trailer?: Trailer): string {
switch (vehicleType) {
case VehicleType.Car:
return `${vehicle.length} ${vehicle.weight}`;
case VehicleType.Pickup:
return `${vehicle.length + trailer!.length} ${vehicle.weight + trailer!.weight}`;
}
}
操场上联系
保证TypeScript知道trailer
不会是undefined
。(如果我们错了,我们将在运行时得到一个"无法读取属性' length ' of null"的错误)。
可以工作,但是许多人不喜欢代码中那种微妙的断言(包括我)。另一个选择是有一个实用函数,你可以使用它来显式地记录你的断言:
function assertNotNullish<T>(value: T | null | undefined): asserts value is T {
if (value ?? null === null) {
throw new Error(`Got null/undefined value where non-null/non-undefined value expected`);
}
}
函数告诉打印稿,如果它完成没有抛出异常,我们传递给它的价值不是null
或undefined
。然后我们像这样使用它:
function vehicle(vehicleType: VehicleType.Car, vehicle: Vehicle): string;
function vehicle(vehicleType: VehicleType.Pickup, vehicle: Vehicle, trailer: Trailer): string;
function vehicle(vehicleType: VehicleType, vehicle: Vehicle, trailer?: Trailer): string {
switch (vehicleType) {
case VehicleType.Car:
return `${vehicle.length} ${vehicle.weight}`;
case VehicleType.Pickup:
assertNotNullish(trailer);
return `${vehicle.length + trailer.length} ${vehicle.weight + trailer.weight}`;
}
}
操场上联系
我建议不要试图处理使第三个参数有条件为空的问题,而是在内部处理它。抛出一个异常,或者结合Nullish Coalescing操作符??
function vehicle(
vehicleType: VehicleType,
vehicle: Vehicle,
trailer?: Trailer
) {
switch (vehicleType) {
case VehicleType.Car:
return `${vehicle.length} ${vehicle.weight}`;
case VehicleType.Pickup:
return `${vehicle.length + (trailer?.length ?? 0)} ${
vehicle.weight + (trailer?.weight ?? 0)
}`;
}
}
这在所有情况下都有效
console.log(vehicle(VehicleType.Car, {weight:100, length:20}))
console.log(vehicle(VehicleType.Pickup, {weight:100, length:20}, {weight:10, length:5}))
console.log(vehicle(VehicleType.Pickup, {weight:100, length:20}))
操场上联系
如果您更喜欢异常路由,检查null的行为意味着您不再从TS
获得可能的null警告。function vehicle(
vehicleType: VehicleType,
vehicle: Vehicle,
trailer?: Trailer
) {
switch (vehicleType) {
case VehicleType.Car:
return `${vehicle.length} ${vehicle.weight}`;
case VehicleType.Pickup:
if(!trailer){
throw "Trailer must be specified when VehicleType is Pickup"
}
return `${vehicle.length + trailer.length} ${
vehicle.weight + trailer.weight
}`;
}
}
操场上联系