Definitelytyped: ts 2.0 ์—„๊ฒฉํ•œ null ๊ฒ€์‚ฌ๋กœ defaultProps ๊ตฌํ˜„

์— ๋งŒ๋“  2016๋…„ 09์›” 30์ผ  ยท  50์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: DefinitelyTyped/DefinitelyTyped

๊ธฐ๋ณธ ์†์„ฑ์€ ํ˜„์žฌ strictNullChecks๊ฐ€ ํ™œ์„ฑํ™”๋œ ์ƒํƒœ์—์„œ ์ œ๋Œ€๋กœ ์ž‘๋™ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด:

interface TestProps { x?: number}

class Test extends React.Component<TestProps, null> {

    static defaultProps =  {x: 5};

    render() {
        const x: number = this.props.x;
        return <p>{x}</p>;
    }
}

๋Ÿฐํƒ€์ž„ ์‹œ ์ž‘๋™์ด ๋ณด์žฅ๋˜์ง€๋งŒ error TS2322: Type 'number | undefined' is not assignable to type 'number' ์˜ค๋ฅ˜.

ํ˜„์žฌ defaultProps์™€ Props๋Š” ํ•ญ์ƒ ๊ฐ™์€ ์œ ํ˜•์œผ๋กœ ์ทจ๊ธ‰๋˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด์ง€๋งŒ Props์˜ ์„ ํƒ์  ํ•„๋“œ๋Š” DefaultProps์˜ ํ•„์ˆ˜ ๊ฐ’์œผ๋กœ ๋ฎ์–ด์จ์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์‹ค์ œ๋กœ๋Š” ๊ฑฐ์˜ ๋™์ผํ•œ ์œ ํ˜•์ด ์•„๋‹™๋‹ˆ๋‹ค.

๋ญ ์ด๋Ÿฐ๊ฒŒ ์žˆ์—ˆ์œผ๋ฉด...

class ComponentWithDefaultProps<P, D, S> {
    props: P & D & {children?: React.Children};
}

props์˜ ์œ ํ˜•์„ ์ œ์™ธํ•˜๊ณ  ๊ธฐ์กด React.Component ํƒ€์ดํ•‘๊ณผ ๋™์ผํ•ฉ๋‹ˆ๊นŒ?

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

์œ ํ˜• ๋ฐ defaultProps์— ๋Œ€ํ•œ ์ข‹์€ ์†”๋ฃจ์…˜์ด ์žˆ๋Š” ์‚ฌ๋žŒ์ด ์žˆ๋‹ค๋ฉด ์ €๋Š” ๋ชจ๋‘ ๊ท€์ž…๋‹ˆ๋‹ค. ํ˜„์žฌ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

interface Props {
  firstName: string;
  lastName?: string;
}

interface DefaultProps {
  lastName: string;
}

type PropsWithDefaults = Props & DefaultProps;

export class User extends React.Component<Props> {
  public static defaultProps: DefaultProps = {
    lastName: 'None',
  }

  public render () {
    const { firstName, lastName } = this.props as PropsWithDefaults;

    return (
      <div>{firstName} {lastName}</div>
    )
  }
}

๋ชจ๋“  50 ๋Œ“๊ธ€

๊ธฐ๋ณธ props๋Š” ๋Ÿฐํƒ€์ž„์— ์„ค์ •๋˜๊ธฐ ๋•Œ๋ฌธ์— type assertion ์™ธ์— ์ด๋ฅผ ์ž˜ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋Š”์ง€ ์ž˜ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค. (๋ฌผ๋ก , ํ•ญ์ƒ ์—„๊ฒฉํ•œ null ๊ฒ€์‚ฌ๋ฅผ ๋น„ํ™œ์„ฑํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.)

๋‹ค์Œ์€ ๊ท€ํ•˜์˜ ์˜ˆ์—์„œ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

interface TestProps { x?: number}

class Test extends React.Component<TestProps, null> {

    static defaultProps =  {x: 5};

    render() {
        const x: number = (this.props.x as number);
        return <p>{x}</p>;
    }
}

https://www.typescriptlang.org/docs/handbook/basic-types.html#type -assertions ์ฐธ์กฐ

์ข€ ๋” ์šฐ์•„ํ•œ ์ฒ˜๋ฆฌ ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค๋ฉด ๋“ฃ๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

๋ฉด์ฑ… ์กฐํ•ญ: ์ €๋Š” ์•ฝ 3์ผ ๋™์•ˆ TypeScript๋ฅผ ์‚ฌ์šฉํ•ด ์™”์œผ๋ฉฐ ํ”ผ๊ณคํ•˜๋ฉฐ ์•„๋งˆ๋„ ์ œ๊ฐ€ ๋ฌด์Šจ ๋ง์„ ํ•˜๊ณ  ์žˆ๋Š”์ง€ ๋ชจ๋ฅผ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

3๊ฐœ์˜ ์ผ๋ฐ˜ ์œ ํ˜• ์ •์˜๋ฅผ ํฌํ•จํ•˜๋„๋ก ์œ ํ˜• ์ •์˜ ํŒŒ์ผ์„ ์—…๋ฐ์ดํŠธํ•  ๋•Œ +1000.

์ด๊ฒƒ์€ ---strictNullChecks ์—†์ด๋„ ๊ดœ์ฐฎ์•˜์ง€๋งŒ ์ง€๊ธˆ์€ ๋งŽ์€ ์ปดํฌ๋„ŒํŠธ ํด๋ž˜์Šค์—์„œ ํ™•์‹คํžˆ ๋ฌธ์ œ๊ฐ€ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

Flow๋Š” ์—„๊ฒฉํ•œ null ์œ ํ˜• ๊ฒ€์‚ฌ์˜ ํŠน์„ฑ ๋•Œ๋ฌธ์— ์œ ์‚ฌํ•œ ํด๋ž˜์Šค ๊ตฌํ˜„๋„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.
https://github.com/facebook/flow/blob/master/lib/react.js#L16
https://github.com/facebook/flow/blob/master/lib/react.js#L104 -L105

์ œ3์˜ ์ œ๋„ค๋ฆญ์„ ์ถ”๊ฐ€ํ•˜๊ธฐ ์œ„ํ•ด https://github.com/Microsoft/TypeScript/issues/2175 ๊ฐ€ ํ•ด๊ฒฐ๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๊ฒƒ ์™ธ์—๋Š” ์˜ต์…˜์ด ๋งŽ์ง€ ์•Š์€ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.
๋‚˜๋Š” ๊ทธ๋Ÿฌํ•œ (ํŒŒ๊ฒฉ์ ์ธ) ๋ณ€๊ฒฝ( class Component<P, S, D> ์„ ์˜๋ฏธํ•จ)์ด ๋ฆฌ๋ทฐ์–ด์— ์˜ํ•ด ์Šน์ธ๋  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
@johnnyreilly @bbenezech @pzavolinsky ์—ฌ๋Ÿฌ๋ถ„์€ ๊ทธ๊ฒƒ์— ๋Œ€ํ•ด ์˜๊ฒฌ์ด ์žˆ์œผ์‹ญ๋‹ˆ๊นŒ?

@r00ger๊ฐ€ ๋™์˜ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ •์˜๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์€ ๋„ˆ๋ฌด ๋ฐฉํ•ด๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

Partial ์‚ฌ์šฉ์„ ๊ณ ๋ คํ•œ ์‚ฌ๋žŒ์ด ์žˆ์Šต๋‹ˆ๊นŒ?

์—์„œ์™€ ๊ฐ™์ด:

    interface ComponentClass<P> {
-        defaultProps?: P;
+        defaultProps?: Partial<P>;
    }

์œ„์˜ Partial ํ•ญ๋ชฉ์€ ์‹ ๊ฒฝ์“ฐ์ง€ ๋งˆ์‹ญ์‹œ์˜ค.

๋ถ€๋ถ„์€ ๋ถ€๋ถ„ propTypes ๋ฌธ์ œ๋ฅผ ์„ ์–ธํ•˜๋Š” ๋ฐฉ๋ฒ•๋งŒ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค. render lastName ๋Š” ์—ฌ์ „ํžˆ string | undefined ์œ ํ˜•์ž…๋‹ˆ๋‹ค. ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋ ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด as ๋˜๋Š” ! ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฌธ์ž์—ด์„ ์บ์ŠคํŒ…ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ž‘๋™ํ•˜์ง€๋งŒ ์ด์ƒ์ ์ด์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค.

interface IUser {
    firstName: string
    lastName?: string
}
export class User extends React.Component<IUser, {}> {
    public static defaultProps: Partial<IUser> = {
        lastName: 'None',
    }

    public render () {
        const { firstName, lastName } = this.props
        // error
        lastName.toUpperCase()

        return (
            <div>{firstName} {lastName}</div>
        )
    }
}

TS๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์‹œ์ž‘ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ๋ญ”๊ฐ€๋ฅผ ๋†“์น˜๊ณ  ์žˆ์Šต๋‹ˆ๊นŒ?

์œ ํ˜• ๋ฐ defaultProps์— ๋Œ€ํ•œ ์ข‹์€ ์†”๋ฃจ์…˜์ด ์žˆ๋Š” ์‚ฌ๋žŒ์ด ์žˆ๋‹ค๋ฉด ์ €๋Š” ๋ชจ๋‘ ๊ท€์ž…๋‹ˆ๋‹ค. ํ˜„์žฌ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

interface Props {
  firstName: string;
  lastName?: string;
}

interface DefaultProps {
  lastName: string;
}

type PropsWithDefaults = Props & DefaultProps;

export class User extends React.Component<Props> {
  public static defaultProps: DefaultProps = {
    lastName: 'None',
  }

  public render () {
    const { firstName, lastName } = this.props as PropsWithDefaults;

    return (
      <div>{firstName} {lastName}</div>
    )
  }
}

+1
๋‚˜๋Š” ํ˜„์žฌ ์ด ๋ฌธ์ œ์™€ ์‹ธ์šฐ๊ณ  ์žˆ๋‹ค.

+1

+1

์„ธ ๋ฒˆ์งธ ์œ ํ˜• ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ ์™ธ์—๋„ ๊ธฐ๋ณธ props์— ๋Œ€ํ•ด props๋ฅผ ๊ตฌ๋ณ„ํ•˜๋Š” ๊ธฐ๋Šฅ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๋‹คํ–‰์Šค๋Ÿฝ๊ฒŒ๋„ TS 2.4๋ถ€ํ„ฐ ์ด๊ฒƒ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค! https://github.com/Microsoft/TypeScript/issues/12215#issuecomment -319495340 ์ฐธ์กฐ

์„ธ ๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” IMHO๋Š” ํฐ ๋ฌธ์ œ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ๋˜ํ•œ Flow ํŒ€๋„ ์ด๋ฅผ ์•Œ๊ณ  ์žˆ์—ˆ๊ณ  ์ตœ๊ทผ์— ์ด๋ฅผ ๋ณ€๊ฒฝํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ข…๋ฅ˜์˜ ์ผ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•„๋Š” ๊ฒƒ์€ ์œ ํ˜• ๊ฒ€์‚ฌ๊ธฐ์˜ ์ฑ…์ž„์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์˜คํ•ดํ•˜์ง€ ๋งˆ์„ธ์š”. ์ €๋Š” Typescript๋ฅผ ์‚ฌ๋ž‘ํ•˜์ง€๋งŒ Flow 0.53๋ถ€ํ„ฐ React ๊ฐœ๋ฐœ์— ๋” ์šฐ์ˆ˜ํ•˜๋‹ค๊ณ  ๋งํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. https://medium.com/flow-type/even-better-support-for-react-in-flow- 25b0a3485627

@Hotell Flow์—๋Š” React.Component ์— ๋Œ€ํ•œ ์„ธ ๊ฐ€์ง€ ์œ ํ˜• ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. - Flow์— ๋งํฌ๋œ Medium ๊ธฐ์‚ฌ์— ๋”ฐ๋ผ ํ•˜์œ„ ํด๋ž˜์Šค ์ฃผ์„์—์„œ ํด๋ž˜์Šค ์œ ํ˜• ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์œ ์ถ”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. - ๊น”๋”ํ•œ ์–ธ์–ด ์ˆ˜์ค€ ๊ธฐ๋Šฅ TS๋Š” ์ง€์›ํ•˜์ง€ ์•Š์ง€๋งŒ ์œ ํ˜•์€ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. -์„ ์–ธ ๊ณ ๋ ค AFAIK.

@aldendaniels

Flow์—๋Š” React.Component์— ๋Œ€ํ•œ ์„ธ ๊ฐ€์ง€ ์œ ํ˜• ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

์•„๋‹ˆ์š”, 0.53 ์ด์ „์—๋Š” ๊ทธ๋žฌ๊ณ  ๋” ์ด์ƒ์€ ์•„๋‹™๋‹ˆ๋‹ค.

@ํ˜ธํ…” ์•„, ๋ฌผ๋ก ์ž…๋‹ˆ๋‹ค! ์ˆ˜์ •ํ•ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

AFAIK๋Š” TS์—์„œ ๊ธฐ๋ณธ ์†Œํ’ˆ์˜ ์œ ํ˜•์„ ์œ ์ถ”ํ•  ๋ฐฉ๋ฒ•์ด ์—†์Šต๋‹ˆ๋‹ค. ์„ธ ๊ฐ€์ง€ ์œ ํ˜•์˜ ๋งค๊ฐœ๋ณ€์ˆ˜ ์ ‘๊ทผ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋ฉด TypeScript ํŒ€์˜ ์—…์ŠคํŠธ๋ฆผ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ฐจ๋‹จํ•˜์ง€ ์•Š๊ณ  ์˜ฌ๋ฐ”๋ฅธ ์ž…๋ ฅ์„ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

typeof MyComponent.defaultProps ๋ฅผ type ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์ „๋‹ฌํ•˜์ง€ ์•Š๊ณ  ์œ ์ถ”๋œ ์œ ํ˜•์˜ ์ •์  ์†์„ฑ์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๊นŒ?

์ด ์ฃผ์ œ์— ๋Œ€ํ•œ ์†Œ์‹์ด ์žˆ์Šต๋‹ˆ๊นŒ? ๋ˆ„๊ตฐ๊ฐ€ ์„ธ ๋ฒˆ์งธ ์œ ํ˜• ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  https://github.com/Microsoft/TypeScript/issues/12215#issuecomment -319495340์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด PR์„ ํ•ฉ๋‹ˆ๊นŒ?

์ฐฌ์„ฑ ๋ฌธ์ œ: ๋™์ผํ•œ ๋ฌธ์ œ

+1

๋‚˜๋Š” ๋˜ํ•œ ์ด๊ฒƒ์— ๋ถ€๋”ช์ณค๊ณ  static defaultProps ์‚ฌ์šฉ์„ ์ž์ œํ•˜๊ณ  ๋Œ€์‹  ๋„์šฐ๋ฏธ HOC๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ๋กœ (์ด ๋ฌธ์ œ๊ฐ€ ์ œ๋Œ€๋กœ ์ˆ˜์ •๋  ๋•Œ๊นŒ์ง€) ์„ ํƒํ–ˆ์Šต๋‹ˆ๋‹ค.

ํŒŒ์ผ ๊ตฌ์„ฑ ์š”์†Œ/helpers/withDefaults.tsx :

import * as React from 'react'

export interface ComponentDefaulter<DP> {
  <P extends {[key in keyof DP]?: any}>(Component: React.ComponentType<P>): React.ComponentType<
    Omit<P, keyof DP> &         // Mandate all properties in P and not in DP
    Partial<Pick<P, keyof DP>>  // Accept all properties from P that are in DP, but use type from P
  >
}

export default function withDefaults<DP>(defaultProps: DP): ComponentDefaulter<DP> {
  return Component => props => <Component {...defaultProps} {...props}/>
}

์ด์ œ ๋‹ค์Œ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํŒŒ์ผ ๊ตฌ์„ฑ ์š”์†Œ/Button.tsx :

import * as React from 'react'
import withDefaults from './helpers/withDefaults'

export interface ButtonProps {
  label: string
  onPress: () => any
}

export const defaultProps = {
  onPress: () => undefined
}

class Button extends React.Component<ButtonProps> {
  // ...
}

export default withDefaults(defaultProps)(Button)

์„ธ ๊ฐ€์ง€ ์ž ์žฌ์ ์ธ ๋‹จ์ (๋‚ด๊ฐ€ ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ๋Š”):

  1. HOC๊ฐ€ ํ•„์š”ํ•˜์ง€๋งŒ ์ด๊ฒƒ์€ React ์„ธ๊ณ„์—์„œ ๋งค์šฐ ์ผ๋ฐ˜์ ์ธ ํŒจ๋Ÿฌ๋‹ค์ž„์ด๊ธฐ ๋•Œ๋ฌธ์— ๊ดœ์ฐฎ์•„ ๋ณด์ž…๋‹ˆ๋‹ค.
  2. props๋ฅผ ์ผ๋ฐ˜ ์œ ํ˜• ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์„ ์–ธํ•ด์•ผ ํ•˜๋ฉฐ props ์†์„ฑ์˜ ์ถ”๋ก ์— ์˜์กดํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
  3. defaultProps ์˜ ์œ ํ˜•์— ๋Œ€ํ•œ ์•”์‹œ์  ๊ฒ€์‚ฌ๋Š” ์—†์ง€๋งŒ export const defaultProps: Partial<ButtonProps> = {...} ๋ฅผ ์ง€์ •ํ•˜์—ฌ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@vsaarinen ์— ๋”ฐ๋ฅด๋ฉด props: Props & DefaultProps ๊ธฐ๋ณธ ํด๋ž˜์Šค๋ฅผ ์ž‘์„ฑํ•˜๋ฏ€๋กœ ๊ธฐ๋ณธ ํด๋ž˜์Šค๋ฅผ ํ™•์žฅํ•˜๋Š” ์ „์ฒด ํด๋ž˜์Šค๋Š” this.props as PropsWithDefaults $ ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  this.props ๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด์™€ ๊ฐ™์ด:

import * as React from 'react'

export class Component<P = {}, S = {}, DP = {}> extends React.Component<P, S> {
  props: Readonly<{ children?: React.ReactNode }> & Readonly<P> & Readonly<DP>
}

export interface Props {
  firstName: string
  lastName?: string
}

export interface DefaultProps {
  lastName: string
}

export class User extends Component<Props, any, DefaultProps> {
  render() {
    const { firstName, lastName } = this.props

    // no error
    return (
      <div>{firstName} {lastName.toUpperCase()}</div>
    )
  }
}

์‹ค์ œ๋กœ @qiu8310 ์€ ์™„์ „ํžˆ ์ž‘๋™ํ•˜์ง€ ์•Š์•˜์ง€๋งŒ ์—ฌ์ „ํžˆ ๊ธฐ๋ณธ ์†Œํ’ˆ์ด ์„ ํƒ ์‚ฌํ•ญ์ด ์•„๋‹ˆ๋ผ๊ณ  ๋น„๋ช…์„ ์ง€๋ฅด๋Š” ํ˜ธ์ถœ ์‚ฌ์ดํŠธ์— ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์•ฝ๊ฐ„์˜ ์กฐ์ •์œผ๋กœ ์ž‘๋™ํ•˜๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.

import * as React from 'react'

export class Component<P = {}, S = {}, DP = {}> extends React.Component<P, S> {
  // Cast the props as something where readonly fields are non optional
  props = this.props as Readonly<{ children?: React.ReactNode }> & Readonly<P> & Readonly<DP>
}

export interface Props {
  firstName: string
  lastName?: string
}

export interface DefaultProps {
  lastName: string
}

export class User extends Component<Props, any, DefaultProps> {
  render() {
    const { firstName, lastName } = this.props

    // no error
    return (
      <div>{firstName} {lastName.toUpperCase()}</div>
    )
  }
}

๋‚˜๋Š” ์ œ 3์˜ ์ œ๋„ค๋ฆญ์„ ๊ฐ€์ง€๊ณ  ๋†€์•˜๊ณ  ๊ฒฐ๊ตญ @qiu8310 ์˜ ์ œ์•ˆ๊ณผ ๋น„์Šทํ•œ ๊ฒƒ์„ ๊ฐ–๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค:

// ComponentWithDefaultProps.ts
import * as React from "react";

export declare class ComponentWithDefaultProps<P, S, DP extends Partial<P>> extends React.Component<P & DP, S> {}
type redirected<P, S, DP> = ComponentWithDefaultProps<P, S, DP>;
const redirected: typeof ComponentWithDefaultProps = React.Component as any;

export const Component = redirected;

// User.ts
import { Component } from "ComponentWithDefaultProps";
export interface Props {
  firstName: string
  lastName?: string
}
export interface DefaultProps {
  lastName: string
}

export class User extends Component<Props, {}, DefaultProps> {
  public render() {
    const { firstName, lastName } = this.props;
    return <div>{firstName} {lastName.toUpperCase()}</div>;
  }
}

๊ทธ๋Ÿฌ๋‚˜ ์ด ๋‘ ๊ฐ€์ง€ ์ ‘๊ทผ ๋ฐฉ์‹(๋‚˜์™€ ์œ„์˜ ์ ‘๊ทผ ๋ฐฉ์‹) ๋ชจ๋‘ ๋” ํฐ ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚ต๋‹ˆ๋‹ค. ๋‚ด ์˜ˆ์—๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์œ ํ˜•์˜ ์ƒ์„ฑ๋œ ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

User: React.ComponentClass<P & DP>
User["props"]: Readonly<{ children?: React.ReactNode }> & Readonly<P & DP>

๋ถ„๋ช…ํžˆ User ์˜ ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์ž˜๋ชป๋˜์—ˆ์Šต๋‹ˆ๋‹ค. React.ComponentClass<P & DP> ๋Š” lastName ๋„ ํ•„์š”ํ•จ์„ ์˜๋ฏธํ•˜๋ฏ€๋กœ

<User firstName="" />;
//    ~~~~~~~~~~~~  Property 'lastName' is missing...

@qiu8310 ์˜ ์˜ˆ์ œ ์œ ํ˜•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋‹ค๋ฆ…๋‹ˆ๋‹ค.

User: React.ComponentClass<P>
User["props"]: Readonly<{ children?: React.ReactNode }> & Readonly<P> & Readonly<DP>

๊ทธ๋Ÿฌ๋‚˜ tsc ์˜ JSX ๊ฒ€์‚ฌ๋Š” props ' type ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋™์ผํ•œ JSX ์กฐ๊ฐ์ด ๋™์ผํ•œ ์˜ค๋ฅ˜๋ฅผ ๋ฐœ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.

<User firstName="John" />;
//    ~~~~~~~~~~~~~~~~  Property 'lastName' is missing...

์žฌ๋ฏธ์žˆ๋Š” ์ ์€ <User firstName="John" /> ๊ฐ€ React.createElement(User, {firstName: "John"}) ๋กœ ๋ฐ”๋€Œ๊ณ  ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋Š” ์œ ํšจํ•œ TypeScript์ž…๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ ์œ ํ˜• ๊ฒ€์‚ฌ๋Š” ComponentClass ์ฒซ ๋ฒˆ์งธ ์œ ํ˜• ๋งค๊ฐœ๋ณ€์ˆ˜์— ์˜์กดํ•˜๋ฏ€๋กœ

<User firstName="Jonh" />; // doesn't work, but
React.createElement(User, { firstName: "John" }); // works

๋ณด์‹œ๋‹ค์‹œํ”ผ ์ œ3์˜ ์ œ๋„ค๋ฆญ์ด ์žˆ๋”๋ผ๋„ ์˜ฌ๋ฐ”๋ฅธ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๋‚ด๋ณด๋‚ด๋ ค๋ฉด ์—ฌ์ „ํžˆ ๋˜ ๋‹ค๋ฅธ ํŠธ๋ฆญ์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

export const User = class extends Component<Props, {}, DefaultProps> {
    // ...
} as React.ComponentClass<Props>;

<User firstName="Jonh" />; // works

๋”ฐ๋ผ์„œ ์ œ3์˜ ์ œ๋„ค๋ฆญ์„ ๊ฐ–๋Š” ๊ฒƒ์€ ๋ณ„๋กœ ์˜๋ฏธ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

React ์˜ ์ •์˜์— ๋ณ‘ํ•ฉํ•  ์ˆ˜ ์žˆ๋Š” ์ข‹์€ ์†”๋ฃจ์…˜์ด ์—†๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ง€๊ธˆ์€ ComponentWithDefaultProps ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ๋‚ด๋ณด๋‚ธ ๊ตฌ์„ฑ ์š”์†Œ์˜ ์œ ํ˜•์„ ์ฃผ์žฅํ•˜๋Š” ๊ฒƒ์„ ๊ณ ์ˆ˜ํ•ฉ๋‹ˆ๋‹ค.

export interface DefaultProps {
    lastName: string;
}
export interface Props extends Partial<DefaultProps> {
    firstName: string;
}

export type PropsWithDefault = Props & DefaultProps;

export const User: as React.ComponentClass<Props> =
class extends React.Component<PropsWithDefault> {
    render() {
        // no error
        return <div>
            {this.props.firstName}
            {this.props.lastName.toUpperCase()}
        </div>;
    }
};
// Note, we've assigned `React.Component<PropsWithDefault>` to `React.ComponentClass<Props>`

๊ทธ ์™ธ์—๋„ ๊ตฌ์„ฑ ์š”์†Œ์˜ ๋ฉ”์„œ๋“œ์—์„œ this.props ์œ ํ˜•์˜ ๋ชจ๋“  ์‚ฌ์šฉ์„ ์ฃผ์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(์˜ˆ: const { lastName } = this.props as Props & DefaultProps ๋˜๋Š” this.props.lastName!.toLowerCase() ์— ๋Š๋‚Œํ‘œ ์‚ฌ์šฉ).

์ด ํ† ๋ก ์— ๋Œ€ํ•œ ๋ช‡ ๊ฐ€์ง€ ์˜ˆ๋ฅผ ์ฐพ์•˜์Šต๋‹ˆ๋‹ค - https://github.com/gcanti/typelevel-ts#objectdiff

HOC ์ ‘๊ทผ ๋ฐฉ์‹(์ €๋Š” ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์„ ํ˜ธํ•ฉ๋‹ˆ๋‹ค)์ด๋ผ๊ณ  ํ•˜๋Š” @rifler ๊ฐ€ ์ž ์‹œ ๋™์•ˆ ์—ฌ๊ธฐ์— ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค . ์šฐ๋ฆฌ๋Š” ๋Ÿฐํƒ€์ž„ ์˜ค๋ฒ„ํ—ค๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š๋Š” ์†”๋ฃจ์…˜์„ ์ œ์‹œํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์˜ค ์ข‹์€
๋‹น์‹ ์ด ํ•ด๊ฒฐ์ฑ…์„ ์ฐพ์„ ์ˆ˜ ์žˆ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค

์–ด๋–ค ์ง„์ „?

๋‹ค์Œ์€ @r00ger ๊ฐ€ ์–ธ๊ธ‰ํ•œ ๊ธฐ์ˆ ์˜ ๋ณ€ํ˜•์ž…๋‹ˆ๋‹ค.

interface IUser {
    name: string;
}
const User = class extends React.Component<IUser> {
    public static defaultProps: IUser = {name: "Foo"}
    public render() {
        return <div>{this.props.name}</div>;
    }
} as React.ComponentClass<Partial<IUser>>;
React.createElement(User, {}); // no error, will output "<div>Foo</div>"

์œ„์˜ ์Šค๋‹ˆํŽซ์„ ์‚ฌ์šฉํ•˜๋ฉด ์ž‘๋™ํ•˜์ง€๋งŒ ์‚ฌ์šฉ์ž๋Š” ์ต๋ช… ํด๋ž˜์Šค๊ฐ€ ๋˜๋ฏ€๋กœ ์ •์  ์†์„ฑ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ํ•ดํ‚ค ์†”๋ฃจ์…˜์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํด๋ž˜์Šค ์ด๋ฆ„์„ ์„€๋„์ž‰ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

// tslint:disable-next-line:no-shadowed-variable
const User = class User extends React.Component<IUser>

์ด์ œ ํด๋ž˜์Šค ๋‚ด์—์„œ ๊ฐœ์ธ์šฉ ์ •์  ํ•„๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ณต๊ฐœ ํ†ต๊ณ„๋Š” ์—ฌ์ „ํžˆ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ tslint๋ฅผ ๋ฌต์Œ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•  ํ•„์š”์„ฑ์— ์ฃผ๋ชฉํ•˜์‹ญ์‹œ์˜ค.

TS 2.8๋ถ€ํ„ฐ Exclude ์œ ํ˜•์ด ๊ณต์‹์ ์œผ๋กœ ์ง€์›๋œ๋‹ค๋Š” ์ ์„ ์–ธ๊ธ‰ํ•  ๊ฐ€์น˜๊ฐ€ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค.

type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;

https://github.com/Microsoft/TypeScript/pull/21847์„ ์ฐธ์กฐํ•˜์„ธ์š”.

๋”ฐ๋ผ์„œ React.createElement() ๊ฐ€ Props ๋Œ€์‹  ๋‹ค์Œ์„ ์š”๊ตฌํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

Omit<Props, keyof DefaultProps>

์œ ์ผํ•œ ๋ฌธ์ œ๋Š” React ์„ ์–ธ์— DefaultProps ์œ ํ˜•์ด ์—†๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด์„œ๋Š” ์„ธ ๋ฒˆ์งธ ์œ ํ˜• ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ํ•„์š”ํ•˜๊ฑฐ๋‚˜ ์ •์  ๋ฉค๋ฒ„ ์œ ํ˜•์„ ์–ธ์–ด ๊ธฐ๋Šฅ์œผ๋กœ ์œ ์ถ”ํ•˜๋Š” ๊ธฐ๋Šฅ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ ๋™์•ˆ ์šฐ๋ฆฌ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ž‘์—…์„ ์ง„ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค.

/**
 * The Create type allow components to implement a strongly thed create() function
 * that alows the caller to omit props with defaults even though the component expects
 * all props to be populated. The TypeScript React typings do not natively support these.
 */
export type Create<C extends BaseComponent<any, any>, D extends {} = {}> = (
  props?: typeHelpers.ObjectDiff<C['props'], D> & React.ClassAttributes<C>,
  ...children: React.ReactNode[]
) => React.ComponentElement<any, any>;

export interface DomPropsType {
  domProps?: domProps.DomProps;
}

export class BaseComponent<P, S = {}> extends React.Component<P & DomPropsType, S> {
  static create(props?: object, ...children: React.ReactNode[]) {
    return React.createElement(this, props, ...children);
  }

  constructor(props: P & DomPropsType, context?: any) {
  ...
}

๊ทธ๋ฆฌ๊ณ  ๋ชจ๋“  ๊ตฌ์„ฑ ์š”์†Œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

export class InsertObjectMenu extends BaseComponent<Props, State> {
  static create: Create<InsertObjectMenu, typeof InsertObjectMenu.defaultProps>;
  static defaultProps = {
    promptForImageUpload: true,
  };
  ...
}

๋งˆ์ง€๋ง‰์œผ๋กœ create ์†์„ฑ์ด ๋ชจ๋“  ๊ตฌ์„ฑ ์š”์†Œ์— ์„ ์–ธ๋˜๋„๋ก ํ•˜๋Š” ๋ฆฐํŠธ ๊ทœ์น™์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” JSX๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ๋‹ค์Œ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

InsertObjectMenu.create({...})

React.createElement() ๋Œ€์‹ .

์šฐ๋ฆฌ๋Š” 1๋…„ ๊ฐ€๊นŒ์ด ๋Œ€๊ทœ๋ชจ ์ฝ”๋“œ๋ฒ ์ด์Šค์—์„œ ์ด ์ ‘๊ทผ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•ด ์™”์œผ๋ฉฐ ์„ฑ๊ณต์ ์œผ๋กœ ์ˆ˜ํ–‰ํ–ˆ์ง€๋งŒ JSX๋ฅผ ์ฑ„ํƒํ•˜๊ณ  ์‹ถ๊ณ  ์ด๊ฒƒ์ด ์šฐ๋ฆฌ๋ฅผ ๊ฐ€๋กœ๋ง‰๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์ด "๊ฐ„๋‹จํ•œ ๋ฌธ์ œ"์— ๋„ˆ๋ฌด ๋งŽ์€ ์‹œ๊ฐ„์„ ํˆฌ์žํ–ˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ https://medium.com/@martin_hotell/ultimate -react-component-patterns-with-typescript-2-8-82990c516935 ๐Ÿ––

    interface Component<P = {}, S = {}, DP extends Partial<P>=P> extends ComponentLifecycle<P, S> { }
    class Component<P, S, DP extends Partial<P> = P> {
        constructor(props: P & DP, context?: any);

        // We MUST keep setState() as a unified signature because it allows proper checking of the method return type.
        // See: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18365#issuecomment-351013257
        // Also, the ` | S` allows intellisense to not be dumbisense
        setState<K extends keyof S>(
            state: ((prevState: Readonly<S>, props: P) => (Pick<S, K> | S | null)) | (Pick<S, K> | S | null),
            callback?: () => void
        ): void;

        forceUpdate(callBack?: () => void): void;
        render(): ReactNode;

        // React.Props<T> is now deprecated, which means that the `children`
        // property is not available on `P` by default, even though you can
        // always pass children as variadic arguments to `createElement`.
        // In the future, if we can define its call signature conditionally
        // on the existence of `children` in `P`, then we should  remove this.
        private __externalProps: Readonly<{ children?: ReactNode }> & Readonly<P>;
        props: Readonly<{ children?: ReactNode }> & Readonly<P> & DP;
        state: Readonly<S>;
        context: any;
        refs: {
            [key: string]: ReactInstance
        };
    }

    class PureComponent<P = {}, S = {}, DP extends Partial<P>=P> extends Component<P, S, P> { }


interface ElementAttributesProperty { __externalProps: {}; }

๋งˆ์ง€๋ง‰ ์ค„์„ ์ž˜ ๋ณด์„ธ์š”.

์ด ๋ณ€๊ฒฝ ์‚ฌํ•ญ์œผ๋กœ ์šฐ๋ฆฌ๋Š”

interface Props {
    a: string
    b?: string
    c?: string
}

class Comp extends React.Component<Props, {}, typeof Comp.defaultProps> {
    static defaultProps = {
        b: ''
    }

    render() {
        const {a, b, c} = this.props

        let res = a.concat(b)  // ok
        let res1 = a.concat(c) //fail

        return null
    }
}



const res1= <Comp a=''/> // ok
const res3 = <Comp /> // fail

static defaultProps ( typeof Comp.defaultProps ๋ฅผ ์ƒ๋žตํ•˜๋ ค๋ฉด ts ๊ฒ€์‚ฌ๊ธฐ๋ฅผ ๋ณ€๊ฒฝํ•ด์•ผ ํ•จ)๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์–ป์„ ์ˆ˜ ์žˆ๋Š” ๊ฐ€์žฅ ์ข‹์€ ๋ฐฉ๋ฒ•์€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?
๋‹ค๋ฅธ ์˜ต์…˜์€ ์ด๋ฏธ ์–ธ๊ธ‰๋˜์—ˆ์Šต๋‹ˆ๋‹ค - HOC, ์œ ํ˜• ์บ์ŠคํŠธ.

๋‹ค์Œ์€ https://medium.com/@martin_hotell/ultimate -react-component-patterns-with-typescript-2-8-82990c516935์˜ ์•„์ด๋””์–ด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•œ ๋‚ด (๋งค์šฐ ๋ชป์ƒ๊ธด) ์‹œ๋„์ž…๋‹ˆ๋‹ค.

type ExtractProps<T> = T extends React.ComponentType<infer Q> ? Q : never;
type ExtractDefaultProps<T> = T extends { defaultProps?: infer Q } ? Q : never;
type RequiredProps<P, DP> = Pick<P, Exclude<keyof P, keyof DP>>;
type RequiredAndPartialDefaultProps<RP, DP> = Required<RP> & Partial<DP>;

type ComponentTypeWithDefaultProps<T> =
  React.ComponentType<
    RequiredAndPartialDefaultProps<
      RequiredProps<ExtractProps<T>, ExtractDefaultProps<T>>,
      ExtractDefaultProps<T>
    >
  >;

function withDefaultProps<T extends React.ComponentType<any>>(Comp: T) {
  return Comp as ComponentTypeWithDefaultProps<T>;
}
interface IProps {
  required: number;
  defaulted: number;
}

class Foo extends React.Component<IProps> {
  public static defaultProps = {
    defaulted: 0,
  };
}

// Whichever way you prefer... The former does not require a function call
const FooWithDefaultProps = Foo as ComponentTypeWithDefaultProps<typeof Foo>;
const FooWithDefaultProps = withDefaultProps(Foo);

const f1 = <FooWithDefaultProps />;  // error: missing 'required' prop
const f2 = <FooWithDefaultProps defaulted={0} />;  // error: missing 'required' prop
const f3 = <FooWithDefaultProps required={0} />;  // ok
const f4 = <FooWithDefaultProps required={0} defaulted={0} />;  // ok

@decademoon , ์ด ์†”๋ฃจ์…˜์„ @types/react ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋‚ด ๋ง์€, ์ผ๋ฐ˜์ ์ธ React.ComponentType ๋ฅผ ๊ท€ํ•˜์˜ ์†”๋ฃจ์…˜์œผ๋กœ ๋Œ€์ฒดํ•˜๋Š” ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค.
๊ทธ๋ ‡๋‹ค๋ฉด PR์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

@decademoon ๊ท€ํ•˜์˜ ์ •์˜๋Š” ๊ธฐ๋ณธ์ด ์•„๋‹Œ ์†Œํ’ˆ์ด ์‹ค์ œ๋กœ ์„ ํƒ์  ํ•„๋“œ๋ฅผ ํฌํ•จํ•˜๋Š” ๊ฒฝ์šฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

interface IProps {
  required: number;
  notRequired?: () => void;
  defaulted: number;
}

class Foo extends React.Component<IProps> {
  public static defaultProps = {
    defaulted: 0,
  };
}

๋‚ด ๊ฒฝ์šฐ์—๋Š” "RP"๋ฅผ "ํ•„์ˆ˜"๋กœ ๋ž˜ํ•‘ํ•˜์ง€ ์•Š๋„๋ก RequiredAndPartialDefaultProps ์œ ํ˜•์„ ๋ณ€๊ฒฝํ•˜์—ฌ ์ž‘๋™ํ•˜๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.

type RequiredAndPartialDefaultProps<RP, DP> = RP & Partial<DP>;

์—ฌ์ „ํžˆ ์ ์ ˆํ•œ ์†”๋ฃจ์…˜์ด ์—†๊ฑฐ๋‚˜ NPM์—์„œ ์ž‘๋™ํ•˜๋Š” HOC๊ฐ€ ์—†๋‹ค๋Š” ์‚ฌ์‹ค์— ๋†€๋ž์Šต๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ๋†“์นœ ๊ฒƒ์ด ์—†๋‹ค๋ฉด.

์•ˆ๋…•ํ•˜์„ธ์š” ์—ฌ๋Ÿฌ๋ถ„. ์ด ์Šค๋ ˆ๋“œ๋ฅผ ์—ฌ์ „ํžˆ ์ฝ๊ณ  ์žˆ๋‹ค๋ฉด @JoshuaToenyes ๊ฐ€ ๊ฐ€์žฅ ์˜๋ฏธ ์žˆ๊ณ  ๋„์›€์ด ๋˜๋Š” ์„ค๋ช…์„ ํ–ˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ํ™•์‹คํžˆ ๋ฌธ์ œ๊ฐ€ ์•„๋‹ˆ๋ฏ€๋กœ ๊ด€๋ จ์ด ์—†์Šต๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ ์œ ํ˜• ์–ด์„ค์…˜์„ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค.

@toiletpatrol ์‹ค์ œ๋กœ @decademoon ์˜ ์†”๋ฃจ์…˜(์•ฝ๊ฐ„์˜ ์ˆ˜์ • ํฌํ•จ)์€ ๊ธฐ๋ณธ ์†Œํ’ˆ์„ ์ž๋™์œผ๋กœ ์ž˜ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ๋ชจ๋“  ์‚ฌ๋žŒ์—๊ฒŒ ๊ธฐ๋Šฅ ํ‘œ์ค€์„ ์ œ๊ณตํ•˜๊ธฐ ์œ„ํ•ด React์˜ DT ์ •์˜์— ํ™•์‹คํžˆ ๋ณ‘ํ•ฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@toiletpatrol @RobRendell ๋ณด์…จ๋‚˜์š” https://github.com/Microsoft/TypeScript/issues/23812?

@vkrol ๋‚˜๋Š” ๊ทธ๊ฒƒ์„ ๋ณด์•˜์ง€๋งŒ, ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์˜ ๋ฆด๋ฆฌ์Šค๋ฅผ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๊ณ  ์ง€๊ธˆ ๋‹น์žฅ ๋‚ด ์ฝ”๋“œ๋ฒ ์ด์Šค์— Decademoon์˜ ๊ตฌํ˜„์„ ์‚ญ์ œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊นŒ๋‹ค๋กœ์šด ๊ฒฝ์šฐ์— ํ˜„์žฌ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š” ๋˜ ๋‹ค๋ฅธ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•:

const restWithDefaults = { ...Component.defaultProps, ...rest };
return <Component {...restWithDefaults} />;

์•„๋ฌด ๋ฌธ์ œ๊ฐ€ ์—†๋Š” ๊ฒƒ ๊ฐ™์œผ๋ฏ€๋กœ ๋”๋Ÿฝ์ง€๋งŒ ๊ฐ„๋‹จํ•œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์œผ๋กœ ์—ฌ๊ธฐ์— ๋‚จ๊ฒจ ๋‘ก๋‹ˆ๋‹ค.

TS 3.2 ๋ฐ react 16.7 ํƒ€์ดํ•‘์ด ์ด ๋ฌธ์ œ๋ฅผ ์ˆ˜์ •ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ซ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

@Hotell ๊ฒฐ๊ตญ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ•ด์•ผํ•ฉ๋‹ˆ๊นŒ? ๋‚˜๋Š” ์•„์ง๋„ ์ด๊ฒƒ์„ ์ œ๋Œ€๋กœ ์ž‘๋™์‹œํ‚ค์ง€ ๋ชปํ•œ๋‹ค.

๋‹ค๋ฅธ ์‚ฌ๋žŒ๋“ค์˜ ์‹œ๊ฐ„์„ ์ ˆ์•ฝํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์Œ์€ Typescript 3์˜ ๋ฆด๋ฆฌ์Šค ์ •๋ณด์— ๋Œ€ํ•œ ๋งํฌ์ž…๋‹ˆ๋‹ค.
JSX์—์„œ defaultProps ์ง€์›

@cbergmiller TypeScript 3.1์˜ ๋ฆด๋ฆฌ์Šค ๋…ธํŠธ์ž…๋‹ˆ๋‹ค ๐Ÿ™ƒ

React.FunctionComponent ์— ์—ฌ์ „ํžˆ ๋™์ผํ•œ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

@denieler React.FunctionComponent defaultProps ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ๊ถŒ์žฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ž์—ฐ์Šค๋Ÿฝ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ธฐ๋ณธ ํ•จ์ˆ˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

interface HelloProps {
  name?: string;
  surname?: string;
}

const HelloComponent: React.FunctionComponent<HelloProps> = ({
  name = 'John',
  surname = 'Smith',
}) => {
  return <div>Hello, {name} {surname}!</div>
};

@mgol props ๋ฅผ ๊ตฌ์กฐํ™”ํ•˜์ง€ ์•Š์œผ๋ ค๋ฉด ๊ธฐ๋ณธ ํ•จ์ˆ˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์–ด๋–ป๊ฒŒ ์ •์˜ํ• ๊นŒ์š”?
๋‹ค์Œ๊ณผ ๊ฐ™์ด "๊ธฐ๋ณธ๊ฐ’" ์†์„ฑ๋งŒ ๊ตฌ์กฐํ™”ํ•˜๋Š” ๊ฒƒ๋งŒ ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

interface HelloProps {
  name?: string;
  surname?: string;
}

const HelloComponent: React.FunctionComponent<HelloProps> = ({
  name = 'John',
  surname = 'Smith',
  ...props
}) => {
  return <div>Hello, {name} {surname}! You are {props.age} years old.</div>
};

ํ•˜์ง€๋งŒ ์ผ๋ถ€ ์†Œํ’ˆ๋งŒ ์ถ”์ถœํ•˜๋Š” ๊ฒƒ์€ ๋ถ€๋„๋Ÿฌ์šด ์ผ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

@glecetre ๋‹ค์Œ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

HelloComponent.defaultProps = {
    name: 'John',
    surname: 'Smith'
}

@Glinkis ๋Š” https://github.com/reactjs/rfcs/pull/107/files#diff -20b9b769068a185d90c23b58a2095a9dR184๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”.

@glecetre ์™œ ๋ชจ๋“  ์†Œํ’ˆ์„ ๋ถ„ํ•ดํ•˜๊ณ  ์‹ถ์ง€ ์•Š์Šต๋‹ˆ๊นŒ? defaultProps ๋ฅผ ์ •์˜ํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ๊ฐ„๋‹จํ•˜๊ณ  ์ž…๋ ฅํ•˜๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค. ํด๋ž˜์Šค ๊ธฐ๋ฐ˜ ๊ตฌ์„ฑ ์š”์†Œ์˜ props ์œ ํ˜•์€ ํ•„์š”ํ•œ props๊ฐ€ defaultProps ์— ํ•ญ๋ชฉ์ด ์žˆ์œผ๋ฉด ๋” ์ด์ƒ ํ•„์š”ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์™ธ๋ถ€์—์„œ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๋‚ด๋ณด๋‚ด๋Š” ๊ฒฝ์šฐ ๋ฌธ์ œ๊ฐ€ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. defaultProps ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๋„ ์•ฝ๊ฐ„ ๋งˆ๋ฒ•์ฒ˜๋Ÿผ ๋ณด์ด์ง€๋งŒ ๋งค๊ฐœ๋ณ€์ˆ˜ ๊ตฌ์กฐํ™”์—์„œ๋Š” ๋ชจ๋‘ JavaScript์ž…๋‹ˆ๋‹ค.

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰