gcanti/newtype-ts

Extends is not working as expected (could be because it's an interface instead of a type)

vinassefranche opened this issue · 0 comments

I'm using typescript@4.3.5.

Extending two different types from the same NewType makes them so their types are considered the same.
See the following example where a variable of type Extends2 can be used as the parameter of a method which declares a type Extends1:

import { Extends, Newtype } from 'newtype-ts';

interface Base extends Newtype<{readonly Base: unique symbol}, number> {}
interface Extends1 extends Extends<Base, {readonly Extends1: unique symbol}>{}
interface Extends2 extends Extends<Base, {readonly Extends2: unique symbol}>{}

declare const methodOnExtends1: (a: Extends1) => void;

declare const extends2: Extends2;
methodOnExtends1(extends2) // does not fail while it should

// things that as working as expected:
declare const methodOnBase: (a: Base) => void;
declare const base: Base;
methodOnExtends1(base); // fails, as expected
methodOnBase(extends2) // does not fail, as expected

What is really strange is that if I 'recompose' the type myself, it works as expected:

declare const methodOnExtends1Recomposed: (a: {_URI: Extends1['_URI'], _A: Extends1['_A']}) => void;
declare const extends2Recomposed: {_URI: Extends2['_URI'], _A: Extends2['_A']};
methodOnExtends1Recomposed(extends2Recomposed); // fails as expected

I found out that declaring Extends as a type instead of an interface fixes the issue:

import { AnyNewtype, Newtype, URIOf, CarrierOf } from 'newtype-ts';

export type Extends<N extends AnyNewtype, Tags extends object> = Newtype<Tags & URIOf<N>, CarrierOf<N>>

interface Base
  extends Newtype<{readonly Base: unique symbol}, number> {}
interface Extends1
  extends Extends<Base, {readonly Extends1: unique symbol}>{}
interface Extends2
extends Extends<Base, {readonly Extends2: unique symbol}>{}

declare const methodOnExtends1: (a: Extends1) => void;
declare const extends2: Extends2;
methodOnExtends1(extends2) // fails, as expected

// non-regression verifications:
declare const methodOnBase: (a: Base) => void;
declare const base: Base;
methodOnExtends1(base); // fails, as expected
methodOnBase(extends2) // does not fail, as expected

Any explanation on this? Should we update the declaration of Extends to be with type (I can do the P.R. if needed)?

Thanks in advance!