我有一个棘手的打字稿问题,我似乎无法解决。我想根据通用来推断值。但是,捕获是推断类型需要来自导入的文件。
在一个示例中解释这是我的通用功能。
import { DocumentNode, print } from "graphql"
type GraphqlRequest = <Query, Variables = {}>({ query, variables }: { query: DocumentNode, variables?: Variables }) => Promise<Query>
const fetchQuery: GraphqlRequest = async ({ query, variables = {} }) => {
return database(JSON.stringify({ query: print(query), variables }))
}
我以下面的方式使用它:
import { UserBlogItems, UserBlogItemsQuery, UserBlogItemsQueryVariables } from "~/graphql"
fetchQuery<UserBlogItemsQuery, UserBlogItemsQueryVariables>({ query: UserBlogItems, variables: { user } })
哪种效果很好,因为我可以在变量和查询响应中查看正确的类型。
我想做的是只需包含 documenode
,并让打字稿查找 QUERY> QUERY
和变量
>从“〜/graphql”文件。这样,我可以使该功能看起来更加清洁。
import { UserBlogItems } from "~/graphql"
fetchQuery({ query: UserBlogItems, variables: { user }})
我可以始终确保〜/graphql
文件中的100%。格式将为:
-
> documentnode:[myquery]
(例如 userblogitems
)
-
查询:[myquery] QUERY
(例如 userblogitemsquery
)
-
code>变量:[[[ myquery] queryVariables
(例如 userblogitemsqueryvariables
)
这在打字稿中是否可以吗?
I have a tricky Typescript problem I can't seem to solve. I would like to infer values based on a generic. However, the catch is the inferred types needs to come from an imported file.
To explain in an example, this is my generic function.
import { DocumentNode, print } from "graphql"
type GraphqlRequest = <Query, Variables = {}>({ query, variables }: { query: DocumentNode, variables?: Variables }) => Promise<Query>
const fetchQuery: GraphqlRequest = async ({ query, variables = {} }) => {
return database(JSON.stringify({ query: print(query), variables }))
}
And I use it in the following way:
import { UserBlogItems, UserBlogItemsQuery, UserBlogItemsQueryVariables } from "~/graphql"
fetchQuery<UserBlogItemsQuery, UserBlogItemsQueryVariables>({ query: UserBlogItems, variables: { user } })
Which works great because I can get the correct types checking on the variables and the query response.
What I would like to be able to do is just include the DocumentNode
and have Typescript look up the Query
and Variables
from the "~/graphql` file. This way I can have the function look a lot cleaner.
import { UserBlogItems } from "~/graphql"
fetchQuery({ query: UserBlogItems, variables: { user }})
I can ensure 100% always that in the ~/graphql
file the format will be:
DocumentNode: [myquery]
(eg UserBlogItems
)
Query: [myquery]Query
(eg UserBlogItemsQuery
)
Variables: [myquery]QueryVariables
(eg UserBlogItemsQueryVariables
)
Is this possible in Typescript?
发布评论
评论(2)
为了为我们推断这些仿制药,我们需要将每个文档节点映射到其各自的查询和查询变量类型。这是通过这样的元素列表来完成的:
第一个元素是文档节点,其次是查询类型,最后是查询变量。您也可以按以下方式编写它并编辑我们以后访问它们以更加明确:
接下来,我们创建的类型将根据给定文档节点为我们检索类型:
为什么
keyof queryTyPemap&amp&amp;字符串
?queryTyPemap
是一个元组,这意味着QUERYTYPEMAP
包括numbern
类型,这会弄乱我们的类型,试图找到正确的查询类型。因此,我们只能通过允许使用&amp;的字符串键来排除它。字符串
。我们还必须注意,
k
,queryTyPemap
的键是 数组的任何键。因此,例如,它可以是indexof
或剪接
。这就是为什么我们必须首先检查queryTyPemap [k]
是元组(这意味着它是我们地图的条目,而不是内置的数组属性)。最后,我们在函数类型中使用此类型:
typeScript将推断通用类型
d
(任何扩展documentnode的类构造函数是type type documentof docrightnode
is),这意味着我们可以在第二个通用类型中直接使用它而不会麻烦。我们将
类型
限制为类型queryTyPemap [number]
,这只能是queryTyPemap
的任何条目。现在,我们从上面的类型将此通用价值赋予了它的价值。您会看到那里有一个额外的
InstanceType
,这是因为d
不是userblogitems
或userInfo
。它实际上是typeof userBlogItems
或type oferInfo
(构造函数)。要获取实例类型,我们使用恰当命名的内置类型InstanceType
。之后,我们坐着很漂亮。我们有正确的查询类型。现在我们只使用它们。
对于
变量的类型
,我们使用类型[2]
获取查询变量类型。对于返回类型,我们使用
类型[1]
获取查询类型。Once again, the playground, so you can tinker with this yourself now that you've got some understanding of it.
For the function to infer these generics for us, we need to map each document node to its respective query and query variables types. This is done with a list of tuples like this:
The first element is the document node, followed by the query type, and finally the query variables. You could also write it as this and edit where we access them later to be more explicit:
Next, we create the type that will retrieve the types for us based on the given document node:
Why is there
keyof QueryTypeMap & string
?QueryTypeMap
is a tuple, which meanskeyof QueryTypeMap
includes thenumber
type, and that would mess with our type trying to find the correct query type. So we can exclude it by only allowing string keys with& string
.We must also be mindful that
K
, the key ofQueryTypeMap
, is any key of an array. So for example, it could beindexOf
orsplice
. That is why we must first check ifQueryTypeMap[K]
is a tuple (meaning it's an entry of our map and not a built-in array property).Lastly, we use this type in our function type:
TypeScript will infer the generic type
D
(any class constructor that extends DocumentNode is whattypeof DocumentNode
is) for us, which means we can directly use it without hassle in the second generic type.We constrain
Types
to the typeQueryTypeMap[number]
, which is saying it can only be any entry ofQueryTypeMap
. Now we give this generic its value with our type from above.You'll see that there's an extra
InstanceType
there, and that's becauseD
is notUserBlogItems
orUserInfo
. It's actuallytypeof UserBlogItems
ortypeof UserInfo
(the constructors). To get the instance types, we use the aptly named built-in typeInstanceType
.And after that, we're sitting pretty. We've got the correct query types. Now we just use them.
For the type of
variables
, we useTypes[2]
to get the query variables type.For the return type, we use
Types[1]
to get the query type.Once again, the playground, so you can tinker with this yourself now that you've got some understanding of it.
而不是通过使用命名惯例来推断类型,而不是做一些黑暗的魔法。您为什么不将从〜/graphQl导出的方式标准化。
这样:
定义这样的提取物:
因此,您最终可以这样使用:
这是Playgorund链接:Playground
Instead of doing some dark magic to infer types by using naming conventions. Why don't you normalize the way you export thing from ~/graphql.
like such:
And the define the fetchQuery like this:
So you finaly can use it like this:
and here is the playgorund link: Playground