e-j5
e-j5

Reputation: 1859

how to fix 'x' is assignable to the constraint of type 'y', but 'y' could be instantiated with a different subtype of constraint 'x'

I am building a utility method that returns the extended type.

Somehow, my approach to building a method has the following compile error.

Type '{ name: string; }' is not assignable to type 'A'. '{ name: string; }' is assignable to the constraint of type 'A', but 'A' could be instantiated with a different subtype of constraint 'Base'.

type Base = {
    name: string;
}

type Ex1 = Base & {other: string};
type Ex2 = Base & {another: string};

type OtherType = {
    name: string
}

const convert = <A extends Base>(other: OtherType): A => {
    const ret = {
        name: other.name
    };
    return ret;
}

const otherType = {name: 'hello'};

const ex1 = convert<Ex1>(otherType);
ex1.other = 'other Value';

const ex2 = convert<Ex2>(otherType);
ex2.another = 'another Value';

see playground

Upvotes: 1

Views: 150

Answers (2)

HTN
HTN

Reputation: 3604

With cast, you can do anything. However, what you want to achieve is the same as:

type Base = {
    name: string;
}

type Ex1 = Base & {other: string};
type Ex2 = Base & {another: string};

type OtherType = {
    name2: string
}

const convertToBase = (other: OtherType): Base => ({
  name: other.name2
});

const otherType = {name2: 'hello'};

const ex1 = convertToBase(otherType) as Ex1;
ex1.other = 'other Value';

const ex2 = convertToBase(otherType) as Ex2;
ex2.another = 'another Value';

With that, at least, you are telling the truth, your method converts OtherType to Base, and not to Ex1 or Ex2. By using cast, you can assign a new property to the object, but you lost the interest of using typescript for type check: If you forgot to assign ex1.other = 'other Value';, your ex1 will still be recongnized as Ex1 by typescript but it's not the truth:

const ex1 = convertToBase(otherType) as Ex1;
// ex1.other = 'other Value'; // forgot to do this
console.log(ex1.other.toUpperCase()); // Got runtime error here

It would be better to do it with destructuring assignment:

const ex1: Ex1 = {
  ...convertToBase(otherType),
  other: 'other Value',
};

const ex2: Ex2 = {
  ...convertToBase(otherType),
  another: 'another Value',
};

const ex3: Ex1 = {
  ...convertToBase(otherType),
  another: 'another Value', // Error here
};

Upvotes: 1

Alireza Ahmadi
Alireza Ahmadi

Reputation: 9903

You have to add Base for your return type too. Like this:

const f = <A extends Base>(a: string): A | Base => {
const ret = {
    name: a
};
   return ret;
}

See Playground

You edit your question so I have to edit my answer!

Edit You can use ret as A; in your convert function. Like this:

const convert = <A extends Base>(other: OtherType): A  => {
const ret = {
    name: other.name
};
return ret as A;
}

See Working sample

Upvotes: 1

Related Questions