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

使用oxide.ts时遇到的TypeScript异常行为问题

问题分析:TypeScript联合类型调用方法时的类型错误

复现步骤

搭建Node.js项目,配置如下:

package.json

{
  "scripts": {
    "start": "tsc && node app.js"
  },
  "devDependencies": {
    "@types/node": "22.5.4",
    "typescript": "5.5.4"
  },
  "dependencies": {
    "oxide.ts": "1.1.0"
  }
}

tsconfig.json

{
  "compilerOptions": {
    "target": "ESNext",
    "module": "commonjs",
    "sourceMap": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "esModuleInterop": true
  }
}

app.ts

import { None, Option } from 'oxide.ts'

export enum Currency {
  EUR = 'EUR',
  UAH = 'UAH',
  USD = 'USD',
}

export interface GenericClientInterface {
  execute(address: string): Promise<void>
}

export interface SpecificClientInterface extends GenericClientInterface {
  lookup(account: string): Promise<boolean>
}

export type CurrencyClientMap = {
  [Currency.EUR]: Option<GenericClientInterface>
  [Currency.UAH]: Option<SpecificClientInterface>
  [Currency.USD]: typeof None
}

export interface CurrencyFactoryInterface {
  build<C extends Currency>(currency: C): CurrencyClientMap[C]
}

export class MyClass{
  public constructor(
    private readonly currencyClientFactory: CurrencyFactoryInterface,
  ) {}

  public execute(currency: Currency): void {
    const clientResult = this.currencyClientFactory.build(currency)

    if (clientResult.isNone()) { // <-- TypeScript error here.
      console.log('clientResult is None')
    }

    const client = clientResult.unwrap()

    console.log('Client: ', client)
  }
}

错误信息

The 'this' context of type 'Option<GenericClientInterface> | Option<SpecificClientInterface> | Readonly<OptionType<never>>' is not assignable to method's 'this' of type 'Option<GenericClientInterface> & Option<SpecificClientInterface> & Option<never>'.
  Type 'Option<GenericClientInterface>' is not assignable to type 'Option<GenericClientInterface> & Option<SpecificClientInterface> & Option<never>'.ts(2684)

原因分析

这个错误是TypeScript类型系统的严格检查结合oxide.ts的类型定义共同导致的:

  1. 联合类型的方法调用规则:调用联合类型值的方法时,TypeScript要求该方法在所有联合成员中都存在,且方法的this参数类型要兼容所有成员。如果不同成员的方法签名(包括this约束)存在差异,TypeScript会要求this是所有成员类型的交集(&),而非并集(|)。

  2. oxide.ts的类型差异:代码中CurrencyClientMap里USD对应的类型是typeof None,实际为Readonly<OptionType<never>>;而EUR、UAH对应的是Option<T>类型。虽然None是Option<never>的实例,但typeof None和Option<never>的类型存在细微差异(比如只读属性约束),导致TypeScript无法将它们归为同一类Option类型。

  3. 类型不兼容的本质:三个类型的isNone()方法,其this约束分别对应各自的类型。TypeScript尝试找一个能同时满足三者的this(即交集),但单个类型无法满足这个交集要求,因此报错。

解决方案

方案1:统一CurrencyClientMap的类型为Option<T>

将USD的类型改为Option<never>(None本质就是Option<never>的实例),让所有成员都属于Option类型的子集:

export type CurrencyClientMap = {
  [Currency.EUR]: Option<GenericClientInterface>
  [Currency.UAH]: Option<SpecificClientInterface>
  [Currency.USD]: Option<never> // 替换typeof None
}

此时build方法的返回类型统一为Option系列类型,TypeScript能正确推断isNone()方法的兼容性。

方案2:手动类型断言

如果不想修改类型定义,可通过断言将clientResult转为通用的Option类型:

if ((clientResult as Option<unknown>).isNone()) {
  console.log('clientResult is None')
}

结论

这个问题不是oxide.ts的bug,而是TypeScript处理联合类型方法调用的严格行为,加上typeof None和Option<never>的类型细微差异引发的。通过统一类型或手动断言即可解决。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 20:05:20