抱歉,标题太复杂了.

我在Typescript 中有这个:

type EthereumAddress = `0x${string}`

type Chain = 'eth'

type ApiMethods = {
  getToken(args: {
    tokenAddress: EthereumAddress
  }): string

  getRank(args: {
    tokenAddress: EthereumAddress
  }): string
}

type ApiPaths<M extends ApiMethods> = {
  [K in keyof M]: {
    path: string
    chain?: Chain
  }
}

const apiPaths = {
  getToken: {
    path: 'tokens/',
    chain: 'eth'
  },

  getRank: {
    path: 'rank/',
  }
} as const satisfies ApiPaths<ApiMethods>

我想构造一个Paths类型,它是api可以采用的所有可能路径的联合.如果apiPaths有一个chain参数,那么路径应该是${path}/${chain},如果不是${path}.

我试着这样做:

type Paths<T extends typeof apiPaths = typeof apiPaths> = {
  [K in keyof T]: T[K] extends {chain: Chain}
    ? `${T[K]['path']}${T[K]['chain']}/`
    : `${T[K]['path']}`
}[keyof T]

但我得到了一个错误:

Type 'T[K]["path"]' is not assignable to type 'string | number | bigint | boolean | null | undefined'.ts(2322)
Type '"path"' cannot be used to index type 'T[K]'.

为什么path不是T[K]的公认参数,如果所有apiPaths值都明确提及,特别是如果apiPaths satisfies ApiPaths<ApiMethods>

推荐答案

Paths<T>类型的问题在于Tgeneric类型constrainedtypeof apiPaths.这意味着它可能是typeof apiPaths中的一些extension,而typeof apiPaths中不存在额外属性,并且没有任何要求这些属性具有path属性. 例如:

type ApiPathsWithRaisins = typeof apiPaths & {raisins: true};
type Oops = Paths<ApiPathsWithRaisins>; 

Paths<T>定义为

type Paths<T extends typeof apiPaths = typeof apiPaths> = {
  [K in keyof T]: T[K] extends { chain: Chain }
  ? `${T[K]['path']}${T[K]['chain']}/`
  : `${T[K]['path']}`
}[keyof T]

那么对于Paths<ApiPathsWithRaisins>K将超过"getToken" | "getRank" | "raisins". 当KraisinsT[K]true,而true没有一个名为path的已知成员.因此,你不能肯定地假设T[K]将具有path属性.

这就是错误的来源.同样,已知typeof apiPaths在每个键上都有path属性,但T没有.


你可以用任何方法来解决它. 如果你只关心计算Paths<typeof apiPaths>,那么实际上根本不需要泛型.只要用typeof apiPaths代替T就可以了:

type MyApiPaths = typeof apiPaths;
type Paths = {
  [K in keyof MyApiPaths]: MyApiPaths[K] extends { chain: Chain }
  ? `${MyApiPaths[K]['path']}${MyApiPaths[K]['chain']}/`
  : `${MyApiPaths[K]['path']}`
}[keyof MyApiPaths]
// type Paths = "rank/" | "tokens/eth/"

或者甚至像

type Paths = keyof { [T in MyApiPaths[keyof MyApiPaths]
  as T extends { chain: infer C extends Chain } ? `${T["path"]}${C}/` : T['path']]: 0
}
// type Paths = "rank/" | "tokens/eth/"

或者,如果你确实想使用泛型,你只需要确保它被适当地约束到每个属性都有一个类似string的属性:

type GoodPaths<T extends Record<keyof T, { path: string }> = typeof apiPaths> = {
  [K in keyof T]: T[K] extends { chain: Chain }
  ? `${T[K]['path']}${T[K]['chain']}/`
  : `${T[K]['path']}`
}[keyof T]
type OK = GoodPaths;
// type OK = "rank/" | "tokens/eth/"

Playground link to code

Typescript相关问答推荐

在Switch陈述中输入保护类

调用另一个函数的函数的Typescript类型,参数相同

如何在方法中定义TypeScript返回类型,以根据参数化类型推断变量的存在?

为什么TypeScript失go 了类型推断,默认为any?''

泛型函数即使没有提供类型

rxjs forkJoin在错误后不会停止后续请求

是否使用非显式名称隔离在对象属性上声明的接口的内部类型?

如何将泛型对象作为返回类型添加到类型脚本中

在另一个类型的函数中,编译器不会推断模板文字类型

是否可以判断某个类型是否已合并为内部类型?

不带其他属性的精确函数返回类型

对象类型的TypeScrip隐式索引签名

为什么&Quot;元素隐式具有';Any';类型,因为...即使我已经打字了,也不能作为索引显示错误吗?

如何使这种一对一关系在旧表上起作用?

如何在具有嵌套数组的接口中允许新属性

映射类型不是数组类型

如何使用 AWS CDK 扩展默认 ALB 控制器策略?

React HashRouter,单击导航栏时页面重新加载出现问题

使用受保护的路由和入门来响应路由

当受其他参数限制时,通用参数类型不会缩小