Definitelytyped: React ꡬ성 μš”μ†ŒλŠ” ReactNodeλ₯Ό λ°˜ν™˜ν•΄μ•Ό ν•˜κ±°λ‚˜ μžμ‹μ€ ReactElementμ—¬μ•Ό ν•©λ‹ˆλ‹€.

에 λ§Œλ“  2017λ…„ 07μ›” 14일  Β·  18μ½”λ©˜νŠΈ  Β·  좜처: DefinitelyTyped/DefinitelyTyped

  • [x] @types/react νŒ¨ν‚€μ§€λ₯Ό μ‚¬μš©ν•΄ λ³΄μ•˜λŠ”λ° λ¬Έμ œκ°€ μžˆμ—ˆμŠ΅λ‹ˆλ‹€.
  • [x] μ•ˆμ •μ μΈ μ΅œμ‹  λ²„μ „μ˜ tscλ₯Ό μ‚¬μš©ν•΄ λ³΄μ•˜μŠ΅λ‹ˆλ‹€. https://www.npmjs.com/package/typescript
  • [x] StackOverflow 에 μ ν•©ν•˜μ§€ μ•Šμ€ 질문이 μžˆμŠ΅λ‹ˆλ‹€. (μ μ ˆν•œ 질문이 있으면 κ±°κΈ°μ—μ„œ μ§ˆλ¬Έν•˜μ‹­μ‹œμ˜€.)
  • [x] [λ©˜μ…˜](https://github.com/blog/821-mention-somebody-they-re-notified) μ €μž( Definitions by: in index.d.ts μ°Έμ‘°) λŒ€λ‹΅ν•˜λ‹€.

    • μ €μž: @ericanderson @onigoetz @tkrotoff @digiguru @morcerf @johnnyreilly @bbenezech @pzavolinsky @DovydasNavickas

λ‹€μŒ ꡬ성 μš”μ†Œ

const SFC: React.StatelessComponent = (props) => props.children;

class C extends React.Component {
  render() {
    return this.props.children;
  }
}

였λ₯˜λ₯Ό μ£Όλ‹€

Type '(props: { children?: ReactNode; }) => ReactNode' is not assignable to type 'StatelessComponent<{}>'.
  Type 'ReactNode' is not assignable to type 'ReactElement<any> | null'.
    Type 'undefined' is not assignable to type 'ReactElement<any> | null'.

Class 'C' incorrectly extends base class 'Component<{}, {}>'.
  Types of property 'render' are incompatible.
    Type '() => ReactNode' is not assignable to type '() => false | Element | null'.
      Type 'ReactNode' is not assignable to type 'false | Element | null'.
        Type 'undefined' is not assignable to type 'false | Element | null'.

Element 에 children λž˜ν•‘ν•˜λ©΄ μž‘λ™ν•©λ‹ˆλ‹€.

children λ₯Ό λ°˜ν™˜ν•˜λŠ” 것은 context 에 무언가λ₯Ό μΆ”κ°€ν•˜λŠ” Provider ꡬ성 μš”μ†Œλ₯Ό λ§Œλ“€ λ•Œ 맀우 μΌλ°˜μ μž…λ‹ˆλ‹€.

κ°€μž₯ μœ μš©ν•œ λŒ“κΈ€

이 문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ ν˜„μž¬ children λ₯Ό React.Fragment 에 λž˜ν•‘ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

λͺ¨λ“  18 λŒ“κΈ€

index.d.ts 에 λŒ€ν•΄ λ‹€μŒ μˆ˜μ •μ„ μ‹œλ„ν–ˆμŠ΅λ‹ˆλ‹€.

-        render(): JSX.Element | null | false;
+        render(): ReactNode;

-        (props: P & { children?: ReactNode }, context?: any): ReactElement<any> | null;
+        (props: P & { children?: ReactNode }, context?: any): ReactNode;

ν•˜μ§€λ§Œ μˆ˜μ •ν•˜λŠ” 방법을 잘 λͺ¨λ₯΄κ² λŠ” 보풀을 μ‹œλ„ν•  λ•Œ μ—¬κΈ°μ €κΈ°μ„œ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.

TypeScript compile error: JSX element type 'ReactNode' is not a constructor function for JSX elements.

μ•„λ‹ˆμš”, 이 λ³€κ²½ 사항은 μ˜¬λ°”λ₯΄μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. ν˜„μž¬ Reactμ—μ„œ μ—¬λŸ¬ μš”μ†Œλ₯Ό λ°˜ν™˜ν•  수 μ—†μŠ΅λ‹ˆλ‹€. μ–΄λ””μ„ κ°€ ν•˜μ‹œλ”λΌλ„ μ•„λ§ˆ μ–΄λ–»κ²Œλ“  ν•˜λ‚˜μ— μΊ‘μŠν™” λ˜μ–΄ μžˆμ„ κ²ƒμž…λ‹ˆλ‹€.
Fiberκ°€ μžˆλŠ” React 16μ—μ„œλ§Œ κ°€λŠ₯ν•©λ‹ˆλ‹€. :)

그듀이 λ°˜μ‘ λ™μž‘μ„ λ³€κ²½ν–ˆλŠ”μ§€ ν™•μ‹€ν•˜μ§€ μ•Šμ§€λ§Œ redux / react μž‘μ„±μžλŠ” https://egghead.io/lessons/ μ—μ„œ λ³Ό 수 μžˆλ“―μ΄ 얇은 래퍼λ₯Ό μƒμ„±ν•˜κΈ° μœ„ν•΄ this.props.children을 λ°˜ν™˜ν•˜λŠ” κΈ°λŠ₯을 λ³΄μ—¬μ£Όμ—ˆμŠ΅λ‹ˆλ‹€(μš”μ†Œμ— childContextλ₯Ό μΆ”κ°€ν•˜κΈ° μœ„ν•΄μ„œλ§Œ).

react-nativeλ₯Ό μ‚¬μš©ν•˜λ©΄ λ Œλ”λ§μ—μ„œ JSX μš”μ†Œμ˜ 배열을 λ°˜ν™˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€. ν•΄κ²° 방법은 this.props.children λ₯Ό React.Children.only(this.props.children) 둜 λž˜ν•‘ν•˜λŠ” 것이며 루트둜 μš”μ†Œκ°€ λ‘˜ 이상 있으면 throwλ©λ‹ˆλ‹€.

λ„€, React.Children.only λ₯Ό μ‚¬μš©ν•  수 μžˆμ§€λ§Œ ReactNode 와 ReactElement λŠ” λͺ¨λ‘ this.props.children 의 μœ νš¨ν•œ κ°’μž…λ‹ˆλ‹€.

const Title = props => <h1>{props.children}</h1>;
<Title>Woah</Title>

μœ νš¨ν•˜κ³  합리적인 React μ½”λ“œμž…λ‹ˆλ‹€.

λ°˜μ‘ 16μ—μ„œ μš”μ†Œ 배열을 λ°˜ν™˜ν•  수 μžˆμ§€λ§Œ TSλŠ” ν•  수 μ—†λ‹€κ³  μƒκ°ν•˜κΈ° λ•Œλ¬Έμ— 였λ₯˜κ°€ λ°œμƒν•©λ‹ˆλ‹€.

νŽΈμ§‘: 이것을 μˆ˜μ •ν•˜λ €λ©΄ children: JSX.Element[]; ν•˜μ„Έμš”.

μ§€κΈˆκΉŒμ§€μ˜ κ²½ν—˜μ— λ”°λ₯΄λ©΄ ReactNodeκ°€ μœ νš¨ν•΄μ•Ό ν•©λ‹ˆλ‹€.

const Id: React.SFC<{}> = (props) => props.children

const App = ()= > <Id>This should work but instead fails</Id>

이것이 @types/react 에 반영될 κ°€λŠ₯성이 μžˆμŠ΅λ‹ˆκΉŒ?

이것이 μ—†μœΌλ©΄ React children을 μž…λ ₯ν•  수 μ—†μŠ΅λ‹ˆλ‹€.

이 문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ ν˜„μž¬ children λ₯Ό React.Fragment 에 λž˜ν•‘ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

μ—…λ°μ΄νŠΈκ°€ μžˆμŠ΅λ‹ˆκΉŒ?

이것이 children λ₯Ό React.Fragment 에 λž˜ν•‘ν•˜λŠ” 것보닀 λ‹€μ†Œ μ •ν™•ν•œμ§€ ν™•μ‹€ν•˜μ§€ μ•Šμ§€λ§Œ 운이 μ’‹μ•˜μŠ΅λ‹ˆλ‹€.

return (children as React.ReactElement);

return <>{foo(}</>; λ°€μƒˆλ„λ‘. <> λŠ” ν•΄κ²° λ°©λ²•μž…λ‹ˆλ‹€.

이것은 λ‚˜μ—κ²Œλ„ 문제λ₯Ό μΌμœΌν‚€κ³  μžˆμŠ΅λ‹ˆλ‹€.

TL;DR:

주어진 ꡬ성 μš”μ†Œμ— μ†Œν’ˆμ„ μΆ”κ°€ν•˜λŠ” HOCκ°€ 있고 return children; μžˆλŠ” κΈ°λŠ₯ ꡬ성 μš”μ†Œμ™€ ν•¨κ»˜ μ‚¬μš©ν•˜λ €κ³  ν•©λ‹ˆλ‹€. ν•΄κ²° 방법 return <>{children}</>; 을 μ‚¬μš©ν•˜λŠ” κ²½μš°μ—λ§Œ μž‘λ™ν•©λ‹ˆλ‹€.

μžμ„Έν•œ λ‚΄μš©μ€:

λ‹€μŒμ€ HOC의 λ‹¨μˆœν™”λœ λ²„μ „μž…λ‹ˆλ‹€.

export interface ExtraProps {
  extraProp1: string;
}

export const withExtraProps = <P extends object>(Component: React.ComponentType<P>) => {
  return class extends React.Component<Omit<P, keyof ExtraProps>> {
    render() {
      const extraProps = {
        extraProp1: 'test'
      };

      return (
        <Component {...this.props as P} {...extraProps} />
      )
    }
  }
}

λ‹€μŒμ€ κΈ°λŠ₯ ꡬ성 μš”μ†Œμ˜ λ‹¨μˆœν™”λœ λ²„μ „μž…λ‹ˆλ‹€.

interface ComponentProps extends ExtraProps {
  children?: React.ReactNode;
  loadingComponent?: ReactElement;
}

export function ExampleComponent(props: ComponentProps) {
  const { children, loadingComponent } = props;

  const [loading, setLoading] = useState(false);

  useEffect(() => {
    const fetchData = async () => {
      setLoading(true);
      await someAsyncCall();
      setLoading(false);
    }

    fetchData();
  });

  if (loading) {
    return loadingComponent || null;
  }

  return children;
}

// This line errors:
export const FeatureFlag = withExtraProps(ExampleComponent);

λ‹€μŒ 였λ₯˜κ°€ λ°œμƒν•©λ‹ˆλ‹€.

Argument of type '(props: ComponentProps) => {} | null | undefined' is not assignable to parameter of type 'ComponentType<ComponentProps>'.
  Type '(props: ComponentProps) => {} | null | undefined' is not assignable to type 'FunctionComponent<ComponentProps>'.
    Type '{} | null | undefined' is not assignable to type 'ReactElement<any, string | ((props: any) => ReactElement<any, string | ... | (new (props: any) => Component<any, any, any>)> | null) | (new (props: any) => Component<any, any, any>)> | null'.
      Type 'undefined' is not assignable to type 'ReactElement<any, string | ((props: any) => ReactElement<any, string | ... | (new (props: any) => Component<any, any, any>)> | null) | (new (props: any) => Component<any, any, any>)> | null'.

ExampleComponent에 ReactElement | null 의 λ°˜ν™˜ μœ ν˜•μ„ μΆ”κ°€ν•˜λ©΄ HOC에 μ „λ‹¬ν•˜λŠ” 것에 λŒ€ν•΄ λΆˆν‰ν•˜μ§€ μ•Šμ§€λ§Œ μžμ‹μ„ λ°˜ν™˜ν•˜λŠ” 경우 μ—¬μ „νžˆ 였λ₯˜κ°€ λ°œμƒν•©λ‹ˆλ‹€.

export function ExampleComponent(props: ComponentProps): ReactElement | null {
  ...
  ...
  return children; // Type 'ReactNode' is not assignable to type 'ReactElement<any, string | ((props: any) => ReactElement<any, string | ... | (new (props: any) => Component<any, any, any>)> | null) | (new (props: any) => Component<any, any, any>)> | null'.

}

여기에 μžˆλŠ” λ‹€λ₯Έ μ‚¬λžŒλ“€κ³Ό λ§ˆμ°¬κ°€μ§€λ‘œ return <>{children}</>; 둜 λ³€κ²½ν•˜λ©΄ μž‘λ™ν•˜μ§€λ§Œ κ±°μΉ κ³  λΆˆν•„μš”ν•˜κ²Œ λŠκ»΄μ§‘λ‹ˆλ‹€.

@kyrstenkelly 와 _μ •ν™•ν•œ_ λ™μΌν•œ λ¬Έμ œκ°€ μžˆμŠ΅λ‹ˆλ‹€. HOC ν•¨μˆ˜κ°€ μžμ‹μ„ λ Œλ”λ§ν•˜λŠ” React.FC μ „λ‹¬λ©λ‹ˆλ‹€.

<> children(...) </> μˆ˜μ •μ΄ μž‘λ™ν•˜μ§€λ§Œ 더 λ‚˜μ€ μ†”λ£¨μ…˜μ΄ 아직 μžˆμŠ΅λ‹ˆκΉŒ?

λ˜ν•œμ΄ λ¬Έμ œκ°€ μžˆμŠ΅λ‹ˆλ‹€! ν˜„μž¬ React.Fragment둜 μ†”λ£¨μ…˜μ„ λž˜ν•‘ν•˜λŠ” μ œμ•ˆ μ†”λ£¨μ…˜μ„ μˆ˜ν–‰ μ€‘μž…λ‹ˆλ‹€.

여기도 λ§ˆμ°¬κ°€μ§€

interface IIntlMessageProps {
    id: string;
    valuesGiven?: {[key:string]:string};
}

// this doesn't work - -- TS2769 Type 'null' is not assignable to type '(nodes:ReactNodeARray) => ReactNode' | ... | undefined
export const IntlMessage: React.FC<IIntlMessageProps> = (props) => {
    return (
        <FormattedMessage id={props.id} values={props.valuesGiven}>
            {props.children}
        </FormattedMessage>
    )
};
//Nope -- TS2769 Type 'Element' is not assignable to type '(nodes:ReactNodeARray) => ReactNode'
export const IntlMessage: React.FC<IIntlMessageProps> = (props) => {
    return (
        <FormattedMessage id={props.id} values={props.valuesGiven}>
            <React.Fragment>{props.children}</React.Fragment>
        </FormattedMessage>
    )
};

// Nope -- TS2769 Type 'Element' is not assignable to type '(nodes:ReactNodeARray) => ReactNode'
export const IntlMessage: React.FC<IIntlMessageProps> | null = (props) => {
    return (
        <FormattedMessage id={props.id} values={props.valuesGiven}>
            <>{props.children}</>
        </FormattedMessage>
    )
};

// Nope -- TS2769 Type '{}' is not assignable to type '(nodes:ReactNodeARray) => ReactNode'
export const IntlMessage: React.FC<IIntlMessageProps> = (props) => {
    return (
        <FormattedMessage id={props.id} values={props.valuesGiven}>
            {props.children ? props.children : undefined}
        </FormattedMessage>
    )
};


λ‚˜λŠ” 여기에 μ‘°μ–Έ 된 λͺ¨λ“  것을 μ‹œλ„ν–ˆλ‹€κ³  μƒκ°ν•©λ‹ˆλ‹€ ... :( μ–΄λ–€ λ‹¨μ„œ?

해결책은 쑰각으둜 포μž₯ν•΄μ•Όν•œλ‹€λŠ” κ²ƒμž…λ‹ˆλ‹€.

import React from 'react'

const Something: React.FC = ({ children }) => (
 <> { children } </>
)

μ΄λ ‡κ²Œ ν•˜λ©΄ Type '({ children }: { children?: ReactNode; }) => ReactNode' is not assignable to type 'FunctionComponent 였λ₯˜κ°€ μˆ˜μ • λ©λ‹ˆλ‹€.

κ°€λŠ₯ν•˜λ©΄ 쑰각을 ν”Όν•˜κ³  JSX μœ ν˜• μ‹œμŠ€ν…œμ΄ JavaScript 및 Flow Reactκ°€ μ§€μ›ν•˜λŠ” 것과 λ™μΌν•œ μ’…λ₯˜μ˜ ꡬ성을 μ§€μ›ν•˜λ„λ‘ ν•˜μ—¬ TypeScriptκ°€ 타사 라이브러리 νŒ¨ν„΄κ³Ό 더 잘 μž‘λ™ν•˜λ„λ‘ ν•˜κ³  μ‹ΆμŠ΅λ‹ˆλ‹€.

λ‚΄κ°€ 찾은 또 λ‹€λ₯Έ 비이상적인 것은 쑰각이 ꡬ성 μš”μ†Œ 트리, 특히 μœ ν‹Έλ¦¬ν‹°/래퍼 ꡬ성 μš”μ†Œ(case-in-point: react-placeholder)μ—μ„œ μ‹¬ν™”λœλ‹€λŠ” κ²ƒμž…λ‹ˆλ‹€. React.ReactElement 둜 μΊμŠ€νŒ…ν•˜λŠ” 것이 μ΄λŸ¬ν•œ μ’…λ₯˜μ˜ λΌμ΄λΈŒλŸ¬λ¦¬μ—μ„œ 더 λ‚˜μ€ μ ‘κ·Ό λ°©μ‹μ²˜λŸΌ λ³΄μ΄μ§€λ§Œ μΊμŠ€νŒ…ν•  ν•„μš”κ°€ μ—†μŠ΅λ‹ˆλ‹€.

ν•΄λ‹Ή μœ ν˜•μ„ λ³€κ²½ν•˜κΈ° μœ„ν•œ μ ‘κ·Ό 방식을 μ‹œλ„ν–ˆμ§€λ§Œ μƒˆλ‘œμš΄ μœ ν˜•μ˜ κΈ°λŠ₯ ꡬ성 μš”μ†Œλ₯Ό μ‚¬μš©ν•˜μ—¬ JSX μœ ν˜• 검사에 κ°‡νžˆκ²Œ λ˜μ—ˆμŠ΅λ‹ˆλ‹€. 아직 지점이 있고, 도움을 μš”μ²­ν•˜λŠ” PR을 ν–ˆλ‹€κ³  λ§Ήμ„Έν•  수 μžˆμ—ˆμ§€λ§Œ λ‹€μ‹œ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€...

μ’‹μŠ΅λ‹ˆλ‹€. μœ ν˜•μ΄ λ…Έλ“œ 배열을 ν—ˆμš©ν•˜μ§€ μ•Šμ•„μ•Ό ν•˜λŠ” 경우 ReactText , ReactPortal , boolean , null 및 undefined λ₯Ό ν—ˆμš©ν•˜μ§€ μ•Šμ•„μ•Ό ν•œλ‹€λŠ” μ˜λ―ΈλŠ” μ•„λ‹™λ‹ˆλ‹€. ReactElement 의 μ‚¬μš©λ²•.

μ΄λŸ¬ν•œ μΆ”κ°€ μœ ν˜•μ„ κΈˆμ§€ν•˜λŠ” μ΄μœ λŠ” λ¬΄μ—‡μž…λ‹ˆκΉŒ?

이 νŽ˜μ΄μ§€κ°€ 도움이 λ˜μ—ˆλ‚˜μš”?
0 / 5 - 0 λ“±κΈ‰