简体   繁体   中英

Defining concrete types for a generic component

I am typing to build an input component which is basically a select for enums in my app, and then define them for the concrete types in a series of one-liners. But whatever I try I seem to hit typing issues. The closest I've got is this:

import React from 'react';

type Props<T> = {
  enumType: T;
  value?: keyof T;
};

type IEnumInput<T = {}> = React.FC<Props<T>>;

const EnumInput: IEnumInput = ({ enumType, value }) => {
  const options = Object.values(enumType).map((x: any) => x.toString());

  return (
    <div>
      Will select from {options.join(',')}, current value is {value}
    </div>
  );
};

type CProps<T> = {
  value?: keyof T;
};

enum MyEnum {
  A = 'a',
  B = 'b',
  C = 'c',
}

const MyEnumInput = (props: React.PropsWithChildren<CProps<MyEnum>>) =>
  EnumInput({ ...props, enumType: MyEnum });

The error I get is

   Type '{ enumType: typeof MyEnum; value?: number | "big" | "link" | "small" | "sub" | "sup" | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | ... 35 more ... | undefined; children?: React.ReactNode; }' is not assignable to type 'Props<{}>'.     Types of property 'value' are incompatible.
       Type 'number | "big" | "link" | "small" | "sub" | "sup" | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | ... 32 more ... | undefined' is not assignable to type 'undefined'.
         Type 'number' is not assignable to type 'undefined'.

Is this the right approach, and if so, what am I getting wrong?

TLDR;

Use typeof MyEnum in place of MyEnum .

Long answer:

You confuse enum as a value with enum as a type.

When you access MyEnum.A you use it as a value, which in transpiled js is a plain object, then access its property '.A'.

When you refer to MyEnum as a type, you're actually referring to the value part of that key-value struct, which in your case is string literal 'a' | 'b' | 'c' 'a' | 'b' | 'c' 'a' | 'b' | 'c' .

If you want to get the type of the plain object MyEnum , you need to use typeof MyEnum .

As a side note, the same thing happens to class in TS.

When declare class Dog {} , Dog is both a type and a value.

When referring to Dog as a type, it's the type of instance of this class. When referring to Dog as a value, it's the Dog class itself, aka, the constructor function, not the instance.

Now if you want to get the type of Dog constructor function, you need to use typeof Dog .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM