skip to Main Content

I have a problem with typescript that say my property isn’t compatible, but I think it is

  • Types of property ‘size’ are incompatible.
  • Type ‘string’ is not assignable to type ‘"s" | "m" | "l" | "xl" | undefined’.ts(2322)

How can I fix it ?

./src/components/App

function App() {
  const params = {
    label: 'Select',
    placeholder: 'Placeholder',
    isRequired: true,
    offset: { X: '0', Y: '100%' },
    zIndex: 10,
    size: 's',
    classes: ['class1', 'class2', 'class3', 'class4', 'class5'],
  };

  return <SelectMenu params={params} />;
}

./src/components/SelectMenu

function SelectMenu({
  params: {
    label,
    placeholder,
    isRequired = false,
    offset = { X: '0', Y: '100%' },
    zIndex = 0,
    size = 'm',
    classes,
  },
}: {
  params: {
    label?: string;
    placeholder?: string;
    isRequired?: boolean;
    offset?: { X: string; Y: string };
    zIndex?: number;
    size?: 's' | 'm' | 'l' | 'xl';
    classes?: string | string[];
  };
})

edit :

I want it to work like that : is this possible ?

  const params = {
    label: 'Select',
    placeholder: 'Placeholder',
    isRequired: true,
    offset: { X: '0', Y: '100%' },
    zIndex: 10,
    size: 's',
    classes: ['class1', 'class2', 'class3', 'class4', 'class5'],
  };

  return <SelectMenu {...params} />;
type Params = {
  label?: string;
  placeholder?: string;
  isRequired?: boolean;
  offset?: { X: string; Y: string };
  zIndex?: number;
  size?: 's' | 'm' | 'l' | 'xl';
  classes?: string | string[];
};

function SelectMenu({
  label,
  placeholder,
  isRequired = false,
  offset = { X: '0', Y: '100%' },
  zIndex = 0,
  size = 'm',
  classes,
}: Params)

2

Answers


  1. if you look at the type of params after you declare it you can see has literal types widened and is

    const params: {
        label: string;
        placeholder: string;
        isRequired: boolean;
        offset: {
            X: string;
            Y: string;
        };
        zIndex: number;
        size: string;
        classes: string[];
    }
    

    so you can either case individual fields to their literal type size: 'm' as 'm' or can add as const to the object

      const params = {
        label: 'Select',
        placeholder: 'Placeholder',
        isRequired: true,
        offset: { X: '0', Y: '100%' },
        zIndex: 10,
        size: 's',
        classes: ['class1', 'class2', 'class3', 'class4', 'class5'],
      } as const;
    

    which gives types

    const params: {
        readonly label: "Select";
        readonly placeholder: "Placeholder";
        readonly isRequired: true;
        readonly offset: {
            readonly X: "0";
            readonly Y: "100%";
        };
        readonly zIndex: 10;
        readonly size: "s";
        readonly classes: readonly [...];
    }
    

    Or if you don’t want to do that if you create params inline in the component render it should accept it

    <SelectMenu params={{
        label: 'Select',
        placeholder: 'Placeholder',
        isRequired: true,
        offset: { X: '0', Y: '100%' },
        zIndex: 10,
        size: 's',
        classes: ['class1', 'class2', 'class3', 'class4', 'class5'],
      }} />
    

    hope that helps

    Login or Signup to reply.
  2. You can try to use a const assertion (as const):

    const params = {
        label: 'Select',
        placeholder: 'Placeholder',
        isRequired: true,
        offset: { X: '0', Y: '100%' },
        zIndex: 10,
        size: 's',
        classes: ['class1', 'class2', 'class3', 'class4', 'class5'],
    } as const;
    

    But note it will affect the resulting type quite a bit, as stated in the docs:

    When declaring a mutable variable or property, TypeScript often widens values to make sure that we can assign things later on without writing an explicit type.

    […]

    To solve this, TypeScript 3.4 introduces a new construct for literal values called const assertions. Its syntax is a type assertion with const in place of the type name (e.g. 123 as const). When we construct new literal expressions with const assertions, we can signal to the language that

    • no literal types in that expression should be widened (e.g. no going from "hello" to string)
    • object literals get readonly properties
    • array literals become readonly tuples

    Or satiesfies:

    import type { Params } from "../SelectMenu";
    
    const params = {
        label: 'Select',
        placeholder: 'Placeholder',
        isRequired: true,
        offset: { X: '0', Y: '100%' },
        zIndex: 10,
        size: 's',
        classes: ['class1', 'class2', 'class3', 'class4', 'class5'],
    } satisfies Params;
    

    Which doesn’t affect the resulting type as much (probably closest to what you want, IMO):

    TypeScript developers are often faced with a dilemma: we want to ensure that some expression matches some type, but also want to keep the most specific type of that expression for inference purposes.

    […]

    The new satisfies operator lets us validate that the type of an expression matches some type, without changing the resulting type of that expression.

    Here you can see the difference between using both:

    declare const paramsAsConst: {
        readonly label: "Select";
        readonly placeholder: "Placeholder";
        readonly isRequired: true;
        readonly offset: {
            readonly X: "0";
            readonly Y: "100%";
        };
        readonly zIndex: 10;
        readonly size: "s";
        readonly classes: readonly ["class1", "class2", "class3", "class4", "class5"];
    };
    
    declare const paramsSatisfies: {
        label: string;
        placeholder: string;
        isRequired: true;
        offset: {
            X: string;
            Y: string;
        };
        zIndex: number;
        size: "s";
        classes: string[];
    };
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search