To fully reproduce the issue, you could set up tiny Node.js project using the following code:
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)
}
}
If you set up a project the way described above, you’ll notice that in line 35 of app.ts
, there is a TypeScript error:
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)
Why is it happening? All three returned types have .isNone()
method. Why A | B | C
is expected to be A & B & C
in the value returned by .build()
method? Is it a TypeScript or Oxide.ts issue?