You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在未知中间类型/名称时创建嵌套字符串字面量联合?

深度嵌套提取ContractName联合类型需求与解决方案

需求背景

需要从深度嵌套的typechainTypes.factories.contracts结构中,提取所有包含contractName属性的类型对应的字符串字面量,形成联合类型。结构示例如下:

typechainTypes.factories.contracts
|
|----->Lock__factory (extends ContractFactory)
|           |-----> contractName: string
|----->****** (这是`export * as foo from bar`导出的模块)
            |-----> ****** (仅提取继承ContractFactory的类型;Extract<Foo, ContractFactory>类型未生效)
                      |-----> contractName: string

期望实现的伪逻辑(支持任意深度):

type ContractFactories = typechainTypes.factories.contracts.**.* extends ContractFactory ? ___ : never;
type ContractNames = ContractFactories['contractName'];

或者递归遍历的伪代码:

type LookForFactory<T> = T.flatMap(foo => {
    if (foo extends ContractFactory)
        return foo;
    else if (foo === undefined || foo === null || foo is SomePrimitive)
        return never;
    else
        return LookForFactory<foo>;
}
type ContractNames = LookForFactory<typechainTypes.factories.contracts>;

最终目标联合类型形式:

typechainTypes.factories.contracts.Lock__factory.contractName
typechainTypes.factories.contracts.SomethingA.this_is_a__factory.contractName
typechainTypes.factories.contracts.SomethingB.SomethingC.this_is_a__factory.contractName
typechainTypes.factories.contracts.*.*.*.*.this_is_a__factory.contractName

若无法支持任意深度,接受以下形式:

typechainTypes.factories.contracts.Lock__factory.contractName
typechainTypes.factories.contracts.UnknownNameA.UnknownNameB__factory.contractName

另外,最初尝试用Parameters<ethers.getContractFactory>[0]实现,但因最后一个重载参数为any,结果变成any[],且未找到排除重载的方法。


方案迭代

初始尝试方案

type LookForFactory<T> = {
  [K in keyof T & string]:
    T[K] extends ContractFactory
      ? K
      : `${K}.${LookForFactory<T[K]>}`
}[keyof T & string]

该方案存在两个问题:

  1. 得到的是完整限定名称(如"somethingA.metadata.opensea.OpenSeaSchema__factory.prototype"),而非contractName的字符串字面量本身;
  2. 部分工厂类型未直接继承ContractFactory但包含contractName属性,无法被正确识别。

调整后的方案

改为直接检查contractName键:

type LookForFactory<T> = {
    [K in keyof T & string]:
    K extends 'contractName'
        ? T[K]
        : LookForFactory<T[K]>
}[keyof T & string];
type ContractNames = LookForFactory<typeof typechainTypes.factories.contracts>;

结果接近预期,但仍包含未展开的LookForFactory<typeof ...>类型,正确结果已部分显示(如"AppProtocolGame"、"Lock")。注意:typechainTypes.factories.contracts.somethingA是通过export * as somethingA from "./somethingA";导出的模块。

最终可行方案

添加NonNullable工具类型后,所有contractName字面量被正确提取:

type LookForFactory<T> = NonNullable<{
    [K in keyof T & string]:
    K extends 'contractName'
        ? T[K]
        : LookForFactory<T[K]>
}[keyof T & string]>;

最终得到的联合类型:

type ContractNames = "OpenSeaSchema" | "JSON" | "JsonTest" | "AppGateway" | "LApp" | "SanctionedRegistry" | "TurnsLimitRule" | "TurnsRule" | "AppProtocolGame" | "GuessANumber" | "GuessANumberRuleCascade" | "TicTacToe" | "TicTacToeRuleCascade" | "Lock"

内容的提问来源于stack exchange,提问作者user3534080

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 03:33:13