类型 'Session' 不可分配给类型 'Record<string, unknown>'



我正在尝试实现https://next-auth.js.org/configuration/callbacks#session-回调。sesssion回调定义如下:

export interface CallbacksOptions {
session?:
| ((session: Session) => WithAdditionalParams<Session>)
| ((session: Session, userOrToken: User | JWT) => Promise<WithAdditionalParams<Session>>);
}

session回调的实现:

async session(session: Session, token: JWT) {
if (token?.accessToken) {
session.user.accessToken = token.accessToken;
session.accessToken = token.accessToken as string;
}
if (token?.provider) {
session.user.provider = token.provider;
}
if (token?.accountId) {
session.user.accountId = token.accountId;
}
return Promise.resolve<WithAdditionalParams<Session>>(session);
}

编译器抱怨:

TS2345: Argument of type 'Session' is not assignable to parameter of type 'WithAdditionalParams<Session> | PromiseLike<WithAdditionalParams<Session>>'.
Type 'Session' is not assignable to type 'WithAdditionalParams<Session>'.
Type 'Session' is not assignable to type 'Record<string, unknown>'.
Index signature is missing in type 'Session'.

session定义如下:

export interface Session {
user: WithAdditionalParams<User>;
accessToken?: string;
expires: string;
}
export type WithAdditionalParams<T extends Record<string, any>> = T & Record<string, unknown>;
export interface User {
name?: string | null;
email?: string | null;
image?: string | null;
}

我做错了什么?下一个身份验证的repo托管在https://github.com/nextauthjs/next-auth和类型https://github.com/DefinitelyTyped/DefinitelyTyped/blob/7c785c26527720bf726f8b8bcbab2f96c600d1a4/types/next-auth/index.d.ts

当您编写时

Promise.resolve<WithAdditionalParams<Session>(session); // error!

您正试图在需要WithAdditionalParams<Session>类型的值的位置使用Session类型的值。由于编译器不认为Session可分配给WithAdditionalParams<Session>,因此存在错误。如果你试图写

Promise.resolve<number>("someString"); // error! string is not a number

你也会有类似的错误。假设您不会考虑在需要number的地方使用string。所以问题是:为什么Session不能分配给WithAdditionalParams<Session>


类型WithAdditionalParams<Session>Session子类型,包括属性为类型unknownstring索引签名。(这就是Record<string, unknown>的意思。(由于Session没有索引签名,编译器不认为WithAdditionalParams<Session>可分配给Session

但是,你可能会认为,session的任何随机额外属性都可以分配给unknown类型所有都可分配给unknown。那么,为什么编译器不把Session当作它有一个字符串索引签名来处理呢?事实上,这种事情确实有时会发生,通过隐式索引签名";。观察:

type SessionType = {
user: WithAdditionalParams<User>;
accessToken?: string;
expires: string;
}
declare const session: SessionType;
Promise.resolve<WithAdditionalParams<Session>>(session); // no error

这里,值session是类型SessionType,即具有与Session相同结构的对象类型的类型别名。编译器非常满意地使用了SessionType,而WithAdditionalParams<Session>是预期的。。。它给CCD_ 29一个隐式索引签名,一切都成功了。

(因此,处理此问题的一种可能方法是使用SessionType而不是Session。(


所以现在的问题是,为什么Session,一个interface没有得到隐式索引签名,而SessionType,一个结构相同的type别名,却得到了?当然,您可能会再次认为,编译器并不是简单地拒绝interface类型的隐式索引签名吗?令人惊讶的是,这正是发生的事情

请参阅microsoft/TypeScript#15300,特别是以下注释:

只是为了填充人员,这种行为目前是故意的。因为接口可以通过额外的声明来扩充,但类型别名不能,所以它是";"更安全";(上面用大引号(来推断类型别名的隐式索引签名,而不是接口。但如果有意义的话,我们也会考虑为接口做这件事

就这样。不能使用Session代替WithAdditionalParams<Session>,因为可能有人会在以后合并与索引签名冲突的属性。这是否是一个令人信服的原因,还有相当激烈的争论,正如你在阅读microsoft/TypeScript#15300时所看到的那样。


那么,我们该如何继续?如果您不想将Sessioninterface更改为type别名,您可以始终使用告诉编译器";我不在乎你是否认为值x不是Y类型,我告诉你它是"也就是说,使用类型断言:

Promise.resolve(session as WithAdditionalParams<Session>); // no error

这是因为编译器将CCD_ 44的类型视为"0";相关的";到WithAdditionalParams<Session>,所以当你断言它实际上那个类型时,编译器会接受你的说法并继续前进。注意,一旦你给session类型WithAdditionalParams<Session>,你就不必在调用Promise.resolve()时手动指定类型参数;编译器会自动推断。

请记住,当您使用类型断言时,您有责任自行验证其准确性。。。由于编译器无法为您验证它,因此它将无法验证您是否正确地完成了断言。如果你的断言不正确,那么你对编译器撒谎了,因此在运行时发生的任何不愉快的事情都是你的错,而不是编译器的错。因此,只有当你相对确信这样做是安全的时,才要小心使用断言

游乐场链接到代码

相关内容

最新更新