Susitha Ravinda Senarath
Susitha Ravinda Senarath

Reputation: 1678

How to fix "but 'T' could be instantiated with a different subtype of constraint ."

myFunc can take either string or number But since argument is defined as T gives error when a string is passed.

interface Props<T> {
  items: T[],
  onValueChange: (vals: T[]) => void;
}

export const myComponent = <T extends string | number>(props: Props<T>) => {

  const {
    items = [],
    onValueChange
  } = props;

  const myFunc = (item: T) => {
    //do something
    console.log(item);
    onValueChange([item])
  }

  items.map(item => myFunc(item));

  myFunc('test');

}

Error

Argument of type 'string' is not assignable to parameter of type 'T'.
  'string' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'string | number'.

How to fix this?

See in: TS Playground

Upvotes: 5

Views: 7899

Answers (2)

smac89
smac89

Reputation: 43138

The compiler message is actually quite clear: You have constrained the myComponent function to only accept props with a resolved type such as string, number, string|number, or never. You have also used the same constraint placed on that function to also restrict what types myFunc can receive (which at this point is whatever was passed into myComponent).

The problem the compiler is trying to draw your attention to is that if myComponent is called with Props<number>, T becomes restricted to only number, which is also what myFunc will be restricted to, therefore if you do myFunc('test'); you may be violating the current constraint on myFunc which is that it only accepts number types, not string.


If you do not wish to adhere to this safeguard, you can shake off the restraints and allow both myFunc and myComponent to become contractually independent of each other, which allows you do what you originally wished to do:

interface Props<T> {
  items: T[],
  onValueChange: (vals: T[]) => void;
}

type ItemType = string | number;

export const myComponent = (props: Props<ItemType>) => {
  const {
    items = [],
    onValueChange
  } = props;

  const myFunc = (item: ItemType) => {
    //do something
    console.log(item);
    onValueChange([item])
  }

  items.map(item => myFunc(item));

  myFunc('test');
}

Another way:

interface Props<T> {
  items: T[],
  onValueChange: (vals: T[]) => void;
}

export const myComponent: React.FC<Props<string | number>> = ({items = [], onValueChange}) => {

  const myFunc = (item: string | number) => {
    //do something
    console.log(item);
    onValueChange([item])
  }

  items.map(item => myFunc(item));

  myFunc('test');

  return {};
}

Upvotes: 3

aleksxor
aleksxor

Reputation: 8340

If it suites your case you can make the type constraint a bit more strict:

export const myComponent = <T extends Props<string | number>>(props: T) => {

  const {
    items = [],
    onValueChange
  } = props;

  const myFunc = (item: string | number) => {
    //do something
    console.log(item);
    onValueChange([item])
  }

  items.map(item => myFunc(item));

  myFunc('test');
}

playground link

Upvotes: 3

Related Questions