Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorrect type resulting type when contains refinement #680

Closed
JalilArfaoui opened this issue Dec 13, 2022 · 3 comments
Closed

Incorrect type resulting type when contains refinement #680

JalilArfaoui opened this issue Dec 13, 2022 · 3 comments

Comments

@JalilArfaoui
Copy link

JalilArfaoui commented Dec 13, 2022

🐛 Bug report

Current Behavior

import * as io from 'io-ts';

export type Brand<T, N> = T & { __brand: N };

export type SensorId = Brand<string, 'SensorId'>;

interface SomeInterface {
  id: SensorId;
}

const InterfaceCodec = io.type({
  id: io.refinement(io.string, (id: string): id is SensorId => true),
});

const codec: io.Type<SomeInterface> = InterfaceCodec;

This generates typing error TS2322 :

TS2322: Type 'TypeC<{ id: RefinementC<StringC, SensorId>; }>' is not assignable to type 'Type<SomeInterface, SomeInterface, unknown>'.
   Types of property 'encode' are incompatible.
        Type 'Encode<{ id: SensorId; }, { id: string; }>' is not assignable to type 'Encode<SomeInterface, SomeInterface>'.
               Type '{ id: string; }' is not assignable to type 'SomeInterface'.
                        Types of property 'id' are incompatible.
                                   Type 'string' is not assignable to type 'SensorId'.
                                                Type 'string' is not assignable to type '{ __brand: "SensorId"; }'.

Expected behavior

I guess that type with refinement should generate a Type that is compatible with the branded type, not just string. So I don’t think we should have an error here … or am I mistaken ?

Your environment

Software Version(s)
io-ts 2.2.20
fp-ts 2.13.1
TypeScript 4.7.4
@JalilArfaoui
Copy link
Author

I think it has something to do with the output type, as I could fix my error by forcing it to unknown :

import * as io from 'io-ts';

export type Brand<T, N> = T & { __brand: N };

export type SensorId = Brand<string, 'SensorId'>;

interface SomeInterface {
  id: SensorId;
}

const InterfaceCodec = io.type({
  id: io.refinement(io.string, (id: string): id is SensorId => true) as io.Type<
    SensorId,
    unknown // <- here
  >,
});

const codec: io.Type<SomeInterface, unknown>  /* <- and here */  = InterfaceCodec;

How can I avoid doing that ? 

Why is refinement constructor not using string (here) as output type ? 

@mlegenhausen
Copy link
Contributor

You want to define a output interface SomeInterfaceOutput like

interface SomeInterfaceOutput {
  id: string;
}

The io.refinement type will be io.Type<SensorId, string> so you can define your codec like

const codec: io.Type<SomeInterface,  SomeInterfaceOutput>  = InterfaceCodec;

When you now encode your interface type io-ts converts the SensorId back to a string. This is all intended behavior.

@JalilArfaoui
Copy link
Author

Thank you for your answer.

Oh, OK, I understand that this is just because output type parameter of Type defaults to A

Actually, I don’t care about the output type interface, I just wanted to validate that my Codec correspond to an existing type, so I wanted to infer it in some way … 

I just found out I can simply use the OutputOf helper instead of unknown :

export type Brand<T, N> = T & { __brand: N };
export type SensorId = Brand<string, 'SensorId'>;

interface SomeInterface {
  id: SensorId;
}

const InterfaceCodec = io.type({
  id: io.refinement(io.string, (id: string): id is SensorId => true),
});

const codec: io.Type<SomeInterface, io.OutputOf<typeof InterfaceCodec>> = InterfaceCodec;

Anyway, thanks for you answer, and thanks for the work of this lib !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants