您希望findUserById()
的输出为User
或User | undefined
,具体取决于输入.但是,函数输出类型依赖于输入类型的唯一方法是将函数设置为overload(为其提供多个调用签名)或将其设置为generic.这两种方法都不能被实现中的编译器验证为类型安全(这超出了编译器的能力范围),因此从调用者的Angular 来看,每种方法都很有用.
传统上,您会使其过载,如下所示:
// call signatures
function findUserById(id: number, options: IOptions & { throwIfNotFound: true }): User;
function findUserById(id: number, options: IOptions): User | undefined;
// implementation
function findUserById(id: number, options: IOptions) {
const { throwIfNotFound = false } = options;
let user: User | undefined;
if (id !== null) {
user = userArray.find((u) => u.id === id);
}
if (throwIfNotFound && !user) {
throw new Error("User not found");
}
return user
}
函数体仅由编译器松散地判断.如果您将判断(throwIfNotFound && !user)
更改为(!throwIfNotFound && !user)
,编译器将无法判断出任何错误.所以要小心.
调用重载函数时,编译器通过按顺序try 每个调用签名直到找到匹配项来解析调用签名,这会给出您正在寻找的行为:
const user1 = findUserById(1, { throwIfNotFound: true });
// ^? const user1: User
const user2 = findUserById(1, {});
// ^? const user2: User | undefined
或者,您可以使该函数成为泛型,并根据输入类型将其返回类型设置为conditional.这种方法有时可能比重载更可取,特别是当输入输出关系太复杂而不能写成一些非通用的调用签名时.
条件类型可能如下所示:
type FindUserById<O extends IOptions> =
User | (O["throwIfNotFound"] extends true ? never : undefined)
其中FindUserById<O>
将始终包含User
,但将不包含其他任何(the never
type)或undefined
,具体取决于输入类型O
constrained到IOptions
是否具有throwIfNotFound
的true
类型(使用the indexed access type O["throwIfNotFound"]
来查找该属性).
则该函数如下所示
function findUserById<O extends IOptions>(id: number, options: O): FindUserById<O> {
const { throwIfNotFound = false } = options;
let user: User | undefined;
if (id !== null) {
user = userArray.find((u) => u.id === id);
}
if (throwIfNotFound && !user) {
throw new Error("User not found");
}
return user as FindUserById<O>
}
同样,编译器无法正确判断函数体.在这里,如果你写了return user
,编译器会抱怨,因为它不能确定user
是否是FindUserById<O>
.因此,我们使用a type assertionas FindUserById<O>
来告诉编译器,我们确信我们知道自己在做什么.同样,这是我们需要小心的事情;同样的问题在!throwIfNotFound
中也会发生.
同样,从呼叫者的Angular 来看,这看起来很好:
const user1 = findUserById(1, { throwIfNotFound: true });
// ^? const user1: User
console.log(user1.id);
const user2 = findUserById(1, {});
// ^? const user2: User | undefined
Playground link to code个