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

TypeScript:如何利用泛型与子类型实现泛型默认值函数?

问题描述

先定义基础与扩展状态接口:

// 基础状态
interface State {
    index: number;
    offset: number;
}

// 扩展基础状态的特殊状态
interface SpecialState extends State {
    search: string;
    filter: string;
}

我需要创建一个函数,接收SpecialState类型的部分对象作为参数:这个参数必须包含扩展出的属性,基础类型的属性可选。为此定义了输入类型:

type InitialState<T extends State> = Partial<State> & Omit<T, keyof State>;

该类型可正常使用,比如创建一个必须包含search和filter、可选包含index和offset的对象:

const initialSpecialState: InitialState<SpecialState> = {
    search: 'hello',
    filter: 'bonjour',
    index: 2
};

函数返回值需要是完整的扩展类型对象(如SpecialState):函数能为缺失的基础属性生成默认值,但无法处理未知的扩展属性,因此扩展属性必须由输入提供。

于是写了支持任意State扩展类型的泛型函数:

function makeFinalState<T extends State>(initialState: InitialState<T>): T {
    const baseState: State = {
        index: 0,
        offset: 0,
    }

    // TypeScript 报错
    const mergedState: T = {
        ...baseState,
        ...initialState
    }

    return mergedState
};

此时TypeScript抛出错误:

'{ index: number; offset: number; } & Partial & Omit<T, keyof State>' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'State'

但实际运行逻辑正常:

const finalState = makeFinalState<SpecialState>(initialSpecialState);
console.log(finalState)
// 输出
// {
//   "index": 2,
//   "offset": 0,
//   "search": "hello",
//   "filter": "bonjour"
// }

请问问题出在哪?有没有其他实现方式?


问题原因与解决方案

报错原因

TypeScript的泛型约束T extends State仅限制T是State的子类型,但无法保证T没有更严格的属性定义。比如若存在如下子类型:

interface StrictState extends State {
    index: 0; // index固定为字面量0
    search: string;
}

此时baseState的index是number类型,虽然值为0,但TypeScript无法确认它完全匹配StrictState中index的字面量类型约束。本质上,TypeScript无法证明{...baseState, ...initialState}能满足T的所有潜在子类型规则。

解决方案

方案1:类型断言(简单直接)

既然你已经确认逻辑正确,可直接用类型断言告知TypeScript合并结果符合T类型:

function makeFinalState<T extends State>(initialState: InitialState<T>): T {
    const baseState: State = {
        index: 0,
        offset: 0,
    }

    const mergedState = {
        ...baseState,
        ...initialState
    } as T;

    return mergedState
};

方案2:重构泛型参数(类型推导更清晰)

将泛型参数T限定为扩展属性部分,而非整个State子类型,让TypeScript能明确推导合并后的类型:

interface State {
    index: number;
    offset: number;
}

// 输入类型:基础属性可选,扩展属性必填
type InitialState<T> = Partial<State> & Required<T>;

function makeFinalState<T>(initialState: InitialState<T>): State & T {
    const baseState: State = {
        index: 0,
        offset: 0,
    }
    return { ...baseState, ...initialState };
}

// 使用示例
interface SpecialState extends State {
    search: string;
    filter: string;
}

const finalState = makeFinalState({
    search: 'hello',
    filter: 'bonjour',
    index: 2
}) as SpecialState;

方案3:优化输入类型定义

用Required严格保证扩展属性必填,同时保留原泛型逻辑,配合类型断言解决报错:

type InitialState<T extends State> = Partial<State> & Required<Omit<T, keyof State>>;

function makeFinalState<T extends State>(initialState: InitialState<T>): T {
    const baseState: State = {
        index: 0,
        offset: 0,
    };
    return { ...baseState, ...initialState } as T;
}

内容的提问来源于stack exchange,提问作者Fredrik Boström

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 17:07:48