简体   繁体   中英

[Typescript]: How to type a generic function to accept a more specific object as argument?

I want to pass a selector to a child component via selector property. The selector property is a generic function which accepts a single object and returns a value. See example below ( playground ).

import React from 'react';

// Some simple state
type State = {
  user: {
    name: string,
    surname: string,
  }
};

// Some simple selector
const getUserName = (state: State) => state.user.name;

type Selector = <RootState = Record<string, unknown>, Selected = unknown>(state: RootState) => Selected;

// React Component Properties
type Properties = {
  selector: Selector;
};

// Component
const Component = (props: Properties) => {
  const { selector } = props;

  /** Do something with the selector */

  return null;
};

// Parent component passing a selector to child
const App = () => {
//   Type '(state: State) => string' is not assignable to type '<RootState = {}, Selected = unknown>(state: RootState) => Selected'.
//   Types of parameters 'state' and 'state' are incompatible.
//     Type 'RootState' is not assignable to type 'State'.(2322)
// input.tsx(16, 3): The expected type comes from property 'selector' which is declared here on type 'IntrinsicAttributes & Properties'
  return <Component selector={getUserName} />
};

It fails to assign an object with specific properties into an object which accepts any properties. Why is that? I encountered this in a different situation and couldn't find a workaround.

(Obviously, I am trying to understand the issue and simply turning these warnings off is not a desired solution.)

You can make the component generic:

// React Component Properties
type Properties<RootState extends Record<string, unknown>, Selected> = {
  selector: (state: RootState) => Selected;
};

// Component
function Component<RootState extends Record<string, unknown>, Selected>(props: Properties<RootState, Selected>) {
  const { selector } = props;

  /** Do something with the selector */

  return null;
}

// Parent component passing a selector to child
const App = () => {
  return <Component selector={getUserName} />
};

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