如何解决打字稿错误“'X' 类型的参数不可分配给 'Y' 类型的参数
How to resolve typescript error "Argument of type 'X' is not assignable to parameter of type 'Y'
更新:Minimum example code reproduction
我有生成以下类型的自动生成代码 (Graphql-Codegen)。为了简单起见,我对其进行了压缩。
export type AccessControlList = {
__typename?: 'AccessControlList';
id: Scalars['Int'];
subscriber: Scalars['Int'];
subscriberRel: MasterSubscriberData;
};
export type Query = {
__typename?: 'Query';
workOrders: Array<WorkOrdersGroupBy>;
viewer: AccessControlAccounts;
};
因为我只对这个例子的查看器感兴趣,所以我 select 使用 Typescript Pick
export type ViewerType = Pick<Query, 'viewer'>;
它还会为需要 AccessControlAccounts 中一些但不是所有可能字段的查询自动生成专用类型,而我需要的字段各不相同。我在这里省略了 MasterSubscriberData 的类型定义,但它可以在下面推断出来(但这个例子并不是真的需要)
不同的查询需要访问查询结构的不同部分,因此为每个不同的查询生成不同的类型,例如
export type LeadSourcesQuery =
{ __typename?: 'Query',
viewer: { __typename?: 'AccessControlAccounts',
AccessControlList: Array<{ __typename?: 'AccessControlList',
subscriberRel: { __typename?: 'MasterSubscriberData',
LeadSources: Array<{ __typename?:
'LeadSources', id: number> }
}>
}
};
我正在尝试创建一个通用函数,它可以 return 对经常访问的深层嵌套对象的引用,而不管传递的子查询类型如何(例如 LeadSourcesQuery
等)。我想出的东西在运行时有效,但未能通过 Typescript 静态检查。
// expected subscriberRel return type
type SubRetType<T extends ViewerType> =
T["viewer"]["AccessControlList"][number]["subscriberRel"];
// returning a reference to subscriberRel to avoid doing this nesting everywhere
export const subscriber = <T extends ViewerType>(
result: T | undefined
): SubRetType<T> | undefined => {
if (!result) return undefined;
return result.viewer?.AccessControlList.at(0)?.subscriberRel;
};
每当我在 src 中调用此代码时,我都会收到以下 Typescript 错误,我知道这与 type compatibility.
有关
Argument of type 'ListCustomerTypesQuery | undefined' is not assignable to
parameter of type 'ViewerType | undefined'.ts(2345)
Types of property 'viewer' are incompatible. ...is missing the
following properties from type AccessControlAccounts
我只是不确定如何帮助 Typescript 理解我传递的任何 T
都将始终是 Query
的子类型。但是,由于不在超类型 Query
上使用 extends
,Typescript 无法理解 AccessControlList.subscriberRel 的嵌套字段存在于“编译时”。并且专用泛型 T
并没有真正扩展超类型 Query
.
每当我不使用泛型时,我可以使用 Extract<Query, ListCustomerTypesQuery>
来提取公共属性,但是当我使用泛型 Extract<Query, T>
时,这不会使错误消失。
我可以通过这样强制转换它来解决这个错误,但我不想每次调用函数都这样做。
subscriber(result.value as ViewerType);
我更愿意清理它并保持原样
subscriber(result.value);
Query
的任何子类型听起来都像是 DeepPartial
的任务。您需要 Query
的任何可能的子类型,这意味着它的所有属性都是可选的。这是 DeepPartial
:
type DeepPartial<T> = T extends object ? {
[P in keyof T]?: DeepPartial<T[P]>;
} : T;
您可能还需要使其适用于数组。
然后我们更改 subscriber
的签名以获取 ViewerType
的深度部分:
export const subscriber = <T extends ViewerType>(
result: DeepPartial<T> | undefined /** Extract<ViewerType,T> doesn't work either */
): SubRetType<T> | undefined => {
不幸的是,您仍然需要转换最后一个 return,因为 TypeScript 不理解您只需要子类型:
return result.viewer?.AccessControlList?.at(0)?.subscriberRel as SubRetType<T>;
更新:Minimum example code reproduction
我有生成以下类型的自动生成代码 (Graphql-Codegen)。为了简单起见,我对其进行了压缩。
export type AccessControlList = {
__typename?: 'AccessControlList';
id: Scalars['Int'];
subscriber: Scalars['Int'];
subscriberRel: MasterSubscriberData;
};
export type Query = {
__typename?: 'Query';
workOrders: Array<WorkOrdersGroupBy>;
viewer: AccessControlAccounts;
};
因为我只对这个例子的查看器感兴趣,所以我 select 使用 Typescript Pick
export type ViewerType = Pick<Query, 'viewer'>;
它还会为需要 AccessControlAccounts 中一些但不是所有可能字段的查询自动生成专用类型,而我需要的字段各不相同。我在这里省略了 MasterSubscriberData 的类型定义,但它可以在下面推断出来(但这个例子并不是真的需要)
不同的查询需要访问查询结构的不同部分,因此为每个不同的查询生成不同的类型,例如
export type LeadSourcesQuery =
{ __typename?: 'Query',
viewer: { __typename?: 'AccessControlAccounts',
AccessControlList: Array<{ __typename?: 'AccessControlList',
subscriberRel: { __typename?: 'MasterSubscriberData',
LeadSources: Array<{ __typename?:
'LeadSources', id: number> }
}>
}
};
我正在尝试创建一个通用函数,它可以 return 对经常访问的深层嵌套对象的引用,而不管传递的子查询类型如何(例如 LeadSourcesQuery
等)。我想出的东西在运行时有效,但未能通过 Typescript 静态检查。
// expected subscriberRel return type
type SubRetType<T extends ViewerType> =
T["viewer"]["AccessControlList"][number]["subscriberRel"];
// returning a reference to subscriberRel to avoid doing this nesting everywhere
export const subscriber = <T extends ViewerType>(
result: T | undefined
): SubRetType<T> | undefined => {
if (!result) return undefined;
return result.viewer?.AccessControlList.at(0)?.subscriberRel;
};
每当我在 src 中调用此代码时,我都会收到以下 Typescript 错误,我知道这与 type compatibility.
有关Argument of type 'ListCustomerTypesQuery | undefined' is not assignable to parameter of type 'ViewerType | undefined'.ts(2345) Types of property 'viewer' are incompatible. ...is missing the following properties from type AccessControlAccounts
我只是不确定如何帮助 Typescript 理解我传递的任何 T
都将始终是 Query
的子类型。但是,由于不在超类型 Query
上使用 extends
,Typescript 无法理解 AccessControlList.subscriberRel 的嵌套字段存在于“编译时”。并且专用泛型 T
并没有真正扩展超类型 Query
.
每当我不使用泛型时,我可以使用 Extract<Query, ListCustomerTypesQuery>
来提取公共属性,但是当我使用泛型 Extract<Query, T>
时,这不会使错误消失。
我可以通过这样强制转换它来解决这个错误,但我不想每次调用函数都这样做。
subscriber(result.value as ViewerType);
我更愿意清理它并保持原样
subscriber(result.value);
Query
的任何子类型听起来都像是 DeepPartial
的任务。您需要 Query
的任何可能的子类型,这意味着它的所有属性都是可选的。这是 DeepPartial
:
type DeepPartial<T> = T extends object ? {
[P in keyof T]?: DeepPartial<T[P]>;
} : T;
您可能还需要使其适用于数组。
然后我们更改 subscriber
的签名以获取 ViewerType
的深度部分:
export const subscriber = <T extends ViewerType>(
result: DeepPartial<T> | undefined /** Extract<ViewerType,T> doesn't work either */
): SubRetType<T> | undefined => {
不幸的是,您仍然需要转换最后一个 return,因为 TypeScript 不理解您只需要子类型:
return result.viewer?.AccessControlList?.at(0)?.subscriberRel as SubRetType<T>;