如何在未知中间类型/名称时创建嵌套字符串字面量联合?
深度嵌套提取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]
该方案存在两个问题:
- 得到的是完整限定名称(如
"somethingA.metadata.opensea.OpenSeaSchema__factory.prototype"),而非contractName的字符串字面量本身; - 部分工厂类型未直接继承
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
相关产品推荐
相关产品推荐

