TypeScript动态调用GraphQL查询时的索引类型错误求助
解决TypeScript索引类型错误:Element implicitly has an 'any' type
错误原因
你遇到的错误是因为TypeScript无法确认动态拼接的gqlQueryName是Queries对象上的合法键——queryStr是任意string类型,导致索引访问时被推断为any类型,触发类型检查错误。
下面是几种不用//@ts-ignore也能解决问题的方案,按类型安全性从高到低排序:
方案一:类型安全的查询映射(推荐)
通过创建输入字符串到查询常量的映射对象,让TypeScript完全推断类型,同时严格限制允许的输入值:
import * as Queries from '../graphql/queries'; import { useQuery } from '@apollo/client'; // 定义仅允许的输入字符串 type QueryInput = 'popular movies' | 'searched movies'; // 建立输入与查询常量的映射 const queryMap: Record<QueryInput, typeof Queries[keyof typeof Queries]> = { 'popular movies': Queries.QUERY_POPULAR_MOVIES, 'searched movies': Queries.QUERY_SEARCHED_MOVIES, }; const UseGetQuery = (queryStr: QueryInput) => { const gqlQuery = queryMap[queryStr]; const { data, loading, refetch } = useQuery(gqlQuery); return { data, loading, refetch }; }; export default UseGetQuery;
这个方案不仅解决了类型错误,还能在你输入非法字符串时直接在编译阶段报错,避免运行时问题。
方案二:类型断言+输入限制
如果不想维护映射对象,可以通过提取Queries的键类型,同时限制输入字符串只能生成合法键:
import * as Queries from '../graphql/queries'; import { useQuery } from '@apollo/client'; // 提取Queries对象的所有合法键类型 type QueryKey = keyof typeof Queries; // 限制输入为能转换成合法键的字符串 type QueryInput = 'popular movies' | 'searched movies'; const UseGetQuery = (queryStr: QueryInput) => { // 断言拼接后的字符串是合法的QueryKey const gqlQueryName = ('QUERY_' + queryStr.toUpperCase().split(' ').join('_')) as QueryKey; const { data, loading, refetch } = useQuery(Queries[gqlQueryName]); return { data, loading, refetch }; }; export default UseGetQuery;
这里通过QueryInput限制输入范围,确保拼接后的gqlQueryName一定是Queries的合法键,再配合类型断言让TypeScript认可这个索引访问。
方案三:添加索引签名(不推荐,牺牲类型安全)
如果确实需要支持任意字符串输入(不建议,会丢失类型检查),可以给Queries添加索引签名:
首先修改../graphql/queries文件,添加类型定义:
import { gql, DocumentNode } from '@apollo/client'; export const QUERY_POPULAR_MOVIES = gql`...`; export const QUERY_SEARCHED_MOVIES = gql`...`; // 添加索引签名,声明所有键为string,值为DocumentNode export type QueryCollection = { [key: string]: DocumentNode; };
然后在辅助函数中使用断言后的类型:
import * as Queries from '../graphql/queries'; import { useQuery } from '@apollo/client'; import type { QueryCollection } from '../graphql/queries'; // 断言Queries符合索引签名类型 const typedQueries = Queries as QueryCollection; const UseGetQuery = (queryStr: string) => { const gqlQueryName = 'QUERY_' + queryStr.toUpperCase().split(' ').join('_'); const { data, loading, refetch } = useQuery(typedQueries[gqlQueryName]); return { data, loading, refetch }; }; export default UseGetQuery;
这个方案会让TypeScript放弃对Queries索引的类型检查,输入错误的queryStr会导致运行时找不到查询常量,所以仅在特殊场景下使用。
内容的提问来源于stack exchange,提问作者Ronny Fitzgerald
相关产品推荐
相关产品推荐

