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

如何定义返回类型由参数值决定的TypeScript泛型函数?

类型安全的符号查找泛型函数实现问题

先给出简化后的基础类型定义:

type Symbol = TypeDef | VarDecl
interface TypeDef
{
  kind: 'type',
  name: string,
  typeData: string;
}
interface VarDecl
{
  kind: 'var',
  name: string,
  varData: number
}
type SymbolKind = Symbol['kind'];

interface SymbolMap
{
  type: TypeDef[],
  var: VarDecl[]
}

现有实现的问题

方案1:传入SymbolKind参数无法缩小返回类型

function LookupSymbol(kind: SymbolKind, map: SymbolMap, name: string)
{
   let symbols = map[kind];
   for(var symbol of symbols)
   {
     if(symbol.name == name)
        return symbol;
   }
}

let x = LookupSymbol('var', m, 'x'); // x: TypeDef | VarDecl | undefined

调用后返回类型仍是联合类型,无法根据传入的kind自动推导对应具体类型。

方案2:使用泛型参数无法匹配SymbolMap对应属性

interface SymbolBase
{
  kind: SymbolKind,
  name: string
}

function LookupSymbol2<T extends SymbolBase>(map: SymbolMap, name: string): T | undefined
{
   let symbols = map[???]; // 无法通过泛型T找到对应的map属性
   for(var symbol of symbols)
   {
     if(symbol.name == name)
        return symbol;
   }
   return undefined;
}

无法从泛型参数T中提取对应的kind值,进而无法访问map的对应属性。

需求是实现支持以下两种调用形式之一的类型安全函数:

  1. LookupSymbol<VarDecl>(map: SymbolMap, name: string): VarDecl | undefined
  2. LookupSymbol(map: SymbolMap, kind: 'var', name: string): VarDecl | undefined

解决方案

方案一:基于SymbolKind参数的类型安全实现

通过泛型约束关联SymbolKind与返回类型,利用条件类型和索引类型实现精准推导:

// 定义kind到对应类型的映射
type SymbolKindToType<K extends SymbolKind> = K extends 'type' ? TypeDef : VarDecl;

function LookupSymbol<K extends SymbolKind>(map: SymbolMap, kind: K, name: string): SymbolKindToType<K> | undefined {
  const symbols = map[kind];
  for (const symbol of symbols) {
    if (symbol.name === name) {
      return symbol as SymbolKindToType<K>;
    }
  }
  return undefined;
}

// 调用示例
const m: SymbolMap = {
  type: [{ kind: 'type', name: 'MyType', typeData: 'data' }],
  var: [{ kind: 'var', name: 'x', varData: 123 }]
};

const x = LookupSymbol(m, 'var', 'x'); // x: VarDecl | undefined
const myType = LookupSymbol(m, 'type', 'MyType'); // myType: TypeDef | undefined

这里的类型断言是安全的,因为map[kind]的类型必然与SymbolKindToType<K>严格对应。

方案二:基于泛型类型参数的实现

通过从泛型T中提取kind属性,结合索引类型访问SymbolMap的对应属性:

// 从符号类型中提取对应的kind值
type SymbolTypeToKind<T extends Symbol> = T['kind'];

function LookupSymbol<T extends Symbol>(map: SymbolMap, name: string): T | undefined {
  const kind = SymbolTypeToKind<T> as keyof SymbolMap;
  const symbols = map[kind] as T[];
  for (const symbol of symbols) {
    if (symbol.name === name) {
      return symbol;
    }
  }
  return undefined;
}

// 调用示例
const x = LookupSymbol<VarDecl>(m, 'x'); // x: VarDecl | undefined
const myType = LookupSymbol<TypeDef>(m, 'MyType'); // myType: TypeDef | undefined

由于T是Symbol的子类型,其kind必然是SymbolMap的合法键,因此类型断言不会存在安全风险。


内容的提问来源于stack exchange,提问作者Антон Игоревич Злыгостев

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 23:10:59