0

I have a constant:

export const LIST_OF_TYPES = {
  TYPE1: 'type1',
  TYPE2: 'type2',
};

In the component, I define the type as follows:

export type IExampleComponentType = 'type1' | 'type2';

interface IExampleComponentProps {
  type: IExampleComponentType;
}

const ExampleComponent: React.FC<IExampleComponentProps> = ({
  type,
}) => {
...

When I try to send a prop to a component like this:

<ExampleComponent type={LIST_OF_TYPES.TYPE1} />

i see an error for prop type like this:

TS2322: Type 'string' is not assignable to type 'IExampleComponentType'.

The expected type comes from property 'type' which is declared here on type 'IntrinsicAttributes & IExampleComponentProps & { children?: ReactNode; }'

What's wrong? How can I fix this?

Jack
  • 514
  • 3
  • 11
  • 30

3 Answers3

1

The type of LIST_OF_TYPES is:

{
    TYPE1: string;
    TYPE2: string;
}

if you declare LIST_OF_TYPES with the as const modifier:

export const LIST_OF_TYPES = {
  TYPE1: 'type1',
  TYPE2: 'type2',
} as const;

its type will, instead be:

{
    readonly TYPE1: "type1";
    readonly TYPE2: "type2";
}

Which is, I believe, what you're looking for.

Further reading: https://stackoverflow.com/a/66993654/14357

spender
  • 117,338
  • 33
  • 229
  • 351
1

When you declare LIST_OF_TYPES like this, it's type is automatically derived as { TYPE1: string; TYPE2: string }, since Typescript supposes you can write some other values into LIST_OF_TYPES's properties in the future. You can prevent this using as const cast:

const LIST_OF_TYPES = {
  ...
} as const;

// also you can now declare IExampleComponentType like this:
type IExampleComponentType = (typeof LIST_OF_TYPES)[keyof typeof LIST_OF_TYPES];

Also you may use a enum here (and it seems like you need one):

enum LIST_OF_TYPES {
  TYPE1 = 'type1',
  TYPE2 = 'type2',
}

// also now IExampleComponentType can be replaced with just LIST_OF_TYPES

Check it out at the playground

Nikita Ivanov
  • 767
  • 5
  • 17
1

A literal is a more concrete sub-type of a collective type. What this means is that "Hello World" is a string, but a string is not "Hello World" inside the type system. - Ts Doc

Typescript will infer string literal types only when you assign it to a const.

Understand it by example

const LIST_OF_TYPES = {
  TYPE1: 'type1',
  TYPE2: 'type2'
};

//string literal
type IExampleType = 'type1' | 'type2';

function myFunction(value: IExampleType) {
  console.log(value);
}

//This will not work
myFunction(LIST_OF_TYPES.TYPE1);

Type Error: Argument of type 'string' is not assignable to parameter of type IExampleType'.

//This will work
const myValue = 'type1';
myFunction(myValue);