3

I have a component with templated props:

const MyComponent = <Value extends any>({ value }: MyComponentProps<Value>) => <div />;

Which I can use without explicitly specifying the type of Value (it is inferred):

<MyComponent value="1" />
<MyComponent value={1} />

I usually write my components this way:

const MyComponent: FunctionComponent<MyComponentProps> = ({ value }) => <div />;

But haven't found a way to template MyComponent and MyComponentProps with this syntax... Does anyone know how to do it? Thanks in advance!

2
  • What exactly is your goal? Commented Aug 1, 2019 at 15:46
  • I have a Tabs component which accepts { views: { key: Key }[], initialView?: Key } so that initialView is always one of views[].key. First syntax works well, but I always use the second and would like to know if it is possible to use it in this case :) Commented Aug 1, 2019 at 15:53

4 Answers 4

2

Based on your comment you just want this:

interface MyComponentProps<V> {
    views: V;
    initialView: keyof V;
}

type KeyedFunctionComponent<T> = FunctionComponent<MyComponentProps<T>>;

const MyComponent: KeyedFunctionComponent<Views> = (views, initialViews) => <div />;

Then declare your function component using either an interface or "typeof views" as the generic argument. Which is good I think. But, what you really want is this combined with a generator, which will allow you to bind and template correctly:

// Declare some views constant:
const views = { home: "home", index: "index" };


// Declare a type for our bound views component
interface KeyedProps<V> {
    initialView?: keyof V;
}


// declare a type for our input function
interface KeyedWithViewsProps<V> extends KeyedProps<V> {
    views: V;
}


// This is the binding function itself
function createKeyedComponent<T>(views: T, toWrap: FunctionComponent<KeyedWithViewsProps<T>>): FunctionComponent<KeyedProps<T>> {
    return (props: KeyedProps<T>) => toWrap({views, initialView: props.initialView});
}


// Call the binder, and pass in the function we want to bind.
const MyComponent = createKeyedCompnonet(views, () => <div />);

// Now, using the component, only requires the optional initialView param, and it it type-checked to ensure it is a member of views
<MyComponent initialView="home" /> // works
<MyComponent initialView="other" /> // doesn't work


Sign up to request clarification or add additional context in comments.

2 Comments

Edited to clarify a bit, hope this is clear but alas I don't know how everything is called in Typescript (yet), which limits my explanations
Take a look at my edit. I think it's what you want.
1

You can extend your ComponentProps like this:

interface MyComponentProps<T> {
   value: T
}

const MyComponent: FunctionComponent<MyComponentProps<string>> = ({ value }) => <div />;

Now value is whatever you pass in <>, for example string or any.

4 Comments

Yes, the problem is the generic is inferred in my first example, and I would like not to pass it to the interface
Can you elaborate on your code more? How do you want to use that component?
Edited to clarify a bit, hope this is clear but alas I don't know how everything is called in Typescript (yet), which limits my explanations
If you just want it to be any (as in your first example), just set value to any: interface MyComponentProps { value: any }
1

This is what I do

export type MyCompProps<ItemType> = {
    item: ItemType;
};

export const MyComp = <ItemType>(props: MyCompProps<ItemType>): React.ReactElement => {
    return <Text>item.toString</Text>;
}

Then I can just call

<MyComp item={myItem} />

And it figures it out.

3 Comments

Works flawlessly, thanks! Can we do that with the const MyComp: VFC<Props> notation?
Also, do you have a Typescript official documentation to refer for this notation?
0

If you want value to be any type:

type MyComponentProps = {
  value: any
};

or just one of explicit types (a.k.a. union type):

type MyComponentProps = {
  value: string | number
};

const MyComponent: FunctionComponent<MyComponentProps> = ({ value }) => <div />;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.