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

TypeScript编译时参数类型检查:值对象类参数未触发错误

问题描述

我用以下值对象(ValueObject)类封装ID:

class UserID {
    constructor(readonly id: number) {}
}

class ProductID {
    constructor(readonly id: number) {}
}

同时定义了如下函数:

function addUser(user : UserID) : Promise<void> {
   //... 执行逻辑
}

const productID = new ProductID(1);
addUser(productID); //<-- 这里居然不会触发构建错误 :(

编译器会接受addUser(new ProductID(1))这样的语句,不会触发构建错误。我知道受JavaScript限制无法在运行时进行类型检查,但为何开发阶段也不会报错?希望通过tsconfig.json配置实现该函数参数的类型检查。


原因分析

TypeScript默认采用结构类型系统(鸭子类型),只要两个类型的结构完全一致,就会被判定为类型兼容。UserID和ProductID都只包含一个只读number类型的id属性,结构完全相同,因此编译器认为两者可以互相替代,不会抛出错误。


解决方案

要让这两个类成为名义类型(仅类本身的标识决定类型兼容性,而非结构),可以通过以下方式实现,配合严格的tsconfig配置强化检查:

1. 给类添加私有/受保护区分属性

给每个类添加一个仅用于类型区分的私有或受保护属性,打破结构一致性:

class UserID {
    private readonly _typeMarker = 'user';
    constructor(readonly id: number) {}
}

class ProductID {
    private readonly _typeMarker = 'product';
    constructor(readonly id: number) {}
}

此时再将ProductID实例传入addUser,编译器会立即抛出类型不兼容的错误。

2. 使用品牌类型(Branded Types)

通过类型别名结合交叉类型,给原始类型添加唯一的“品牌”标记,无需类实现:

type UserID = number & { readonly _brand: unique symbol };
type ProductID = number & { readonly _brand: unique symbol };

function createUserID(id: number): UserID {
    return id as UserID;
}

function createProductID(id: number): ProductID {
    return id as ProductID;
}

function addUser(user : UserID) : Promise<void> {
   //... 执行逻辑
}

const productID = createProductID(1);
addUser(productID); // 此处触发类型错误

3. 优化tsconfig配置强化检查

确保tsconfig.json启用严格类型检查选项,虽然无法直接解决结构兼容问题,但能强化整体类型校验:

{
  "compilerOptions": {
    "strict": true,
    "strictFunctionTypes": true,
    "noImplicitAny": true
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 14:12:25