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

TypeScript中如何从资源类型自动推导其翻译泛型类型?

简化TypeScript翻译属性获取器的泛型声明

问题背景

我定义了以下TypeScript接口,其中Translation是翻译项的基础接口,Resource作为带翻译的资源基类,Product是具体的资源类型:

interface Translation {
    locale: string
}

interface Resource<T extends Translation> {
    translations: T[]
}

interface ProductTranslation {
  locale: string,
  title: string,
  status: number
}

interface Product {
  translations: ProductTranslation[]
}

我想要实现一个生成器函数,用来创建获取指定翻译属性的函数,原生JavaScript版本如下:

function getTranslationValue(locale, key) {
  return (resource) => {
    const translation = resource.translations.find(t => t.locale === locale)
    return (translation && translation[key]) || null
  }
}

目前我在TypeScript中的实现需要声明两个泛型参数,调用时必须同时传入资源类型和对应的翻译类型,代码如下:

function getTranslationValue<R extends Resource<T>, T extends Translation>(
    locale: R['translations'][number]['locale'], 
    key: keyof R['translations'][number]
) {
    return (resource: R) => {
        const translation = resource.translations.find(t => t.locale === locale)
        const value = translation && translation[key]
        return value ?? null
    }
}

调用示例:

const getTitle = getTranslationValue<Product, Product['translations'][number]>('en', 'title')
getTitle(myProduct)

我希望能省略Product['translations'][number]这个泛型参数,让调用简化为:

const getTitle = getTranslationValue<Product>('en', 'title')
getTitle(myProduct)

解决方案

可以通过从资源类型R中自动推导翻译项类型,来省略第二个泛型参数。以下是两种可行的实现方式:

方式一:保留Translation约束的简化写法

function getTranslationValue<R extends { translations: T[] }, T extends Translation>(
    locale: T['locale'],
    key: Exclude<keyof T, 'locale'> // 可选:限制key为非locale的翻译属性
) {
    return (resource: R) => {
        const translation = resource.translations.find(t => t.locale === locale)
        return translation?.[key] ?? null
    }
}

方式二:直接从R提取翻译类型

如果不需要依赖Resource接口,也可以直接从资源类型中提取翻译项的结构:

function getTranslationValue<R extends { translations: Array<{ locale: string; [key: string]: unknown }> }>(
    locale: R['translations'][number]['locale'],
    key: Exclude<keyof R['translations'][number], 'locale'>
) {
    return (resource: R) => {
        const translation = resource.translations.find(t => t.locale === locale)
        return translation?.[key] ?? null
    }
}

简化后的调用

现在只需要传入资源类型作为唯一泛型参数,TypeScript会自动推导翻译项的合法属性:

// 合法调用:key为ProductTranslation的title或status
const getTitle = getTranslationValue<Product>('en', 'title')
const getStatus = getTranslationValue<Product>('en', 'status')

// 非法调用(会触发TS报错):locale不是有效的属性(如果用了Exclude约束)
// const getLocale = getTranslationValue<Product>('en', 'locale')

// 测试示例
const myProduct: Product = {
    translations: [
        { locale: 'en', title: 'Apple', status: 1 },
        { locale: 'zh', title: '苹果', status: 1 }
    ]
}

console.log(getTitle(myProduct)) // 输出:Apple
console.log(getStatus(myProduct)) // 输出:1
console.log(getTranslationValue<Product>('fr', 'title')(myProduct)) // 输出:null

原理说明

  1. 仅保留R作为泛型参数,约束R必须包含translations数组属性
  2. 通过R['translations'][number]自动提取数组中单个翻译项的类型,无需显式声明T
  3. 使用Exclude<keyof ..., 'locale'>可以限制key只能是翻译项的业务属性,避免无意义的调用

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 03:57:45