Typescript: ๊ธฐ๋Šฅ ๊ตฌ์„ฑ ์š”์†Œ์—์„œ ์ž‘๋™ํ•˜์ง€ ์•Š๋Š” ๊ธฐ๋ณธ ์†Œํ’ˆ

์— ๋งŒ๋“  2019๋…„ 05์›” 04์ผ  ยท  16์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: microsoft/TypeScript

TypeScript ๋ฒ„์ „ : 3.4.5

๊ฒ€์ƒ‰์–ด:
๋ฐ˜์‘ ๊ธฐ๋ณธ ์†Œํ’ˆ defaultProps ๊ธฐ๋Šฅ ๊ตฌ์„ฑ ์š”์†Œ ์ƒํƒœ ๋น„ ์ €์žฅ

์•”ํ˜ธ

import React from "react";

interface Props {
  name: string;
  optional: string;
}

const Component = ({ name, optional = "default" }: Props) => (
  <p>{name + " " + optional}</p>
);

const Test = () => <Component name="test" />;

์˜ˆ์ƒ๋˜๋Š” ๋™์ž‘ :
์— ๋”ฐ๋ฅด๋ฉด ํƒ€์ดํ”„ ๋ผ์ดํ„ฐ 3.0 ๋ฆด๋ฆฌ์Šค ๋…ธํŠธ ๋Š” optional ์†Œํ’ˆ์ด ํ•„์š”ํ•˜์ง€ ๋ง์•„์•ผ Test ๊ทธ๊ฒƒ์ด ES2015 ๊ธฐ๋ณธ ์ดˆ๊ธฐํ™”๊ฐ€์—์„œ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ธฐ๋ณธ์œผ๋กœ ์ •์˜ ๋œ๋Œ€๋กœ Component .

์‹ค์ œ ํ–‰๋™ :
๋‹ค์Œ ์ปดํŒŒ์ผ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

'{name : string; ์œ ํ˜•์—'optional '์†์„ฑ์ด ์—†์Šต๋‹ˆ๋‹ค. } '์ด์ง€๋งŒ'Props '์œ ํ˜•์—๋Š” ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

ํ”Œ๋ ˆ์ด ๊ทธ๋ผ์šด๋“œ ๋งํฌ :
๋งํฌ
๋†€์ดํ„ฐ๊ฐ€ TSX๋ฅผ ์ง€์›ํ•˜๋Š” ๊ฒƒ ๊ฐ™์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๊ด€๋ จ ๋ฌธ์ œ:

27425

Question

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

TypeScript 3.1 ์ด์ƒ์—์„œ๋Š” ๋‹ค์Œ์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค.

export interface Props {
    name: string;
}

function Greet(props: Props) {
    return <div>Hello {props.name.toUpperCase()}!</div>;
}
Greet.defaultProps = {
  name: "world",
} as Partial<Props>; // Good practice, without it you could use a number as default prop and TypeScript wouldn't complainโ€ฆ

const component = <Greet />; // no errors

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

์ด์— ๋Œ€ํ•œ ์„ค๋ช…์„œ๊ฐ€ ๋ช…ํ™•ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์ง€๋งŒ Props ์ธํ„ฐํŽ˜์ด์Šค์—์„œ ์†์„ฑ์„ ์„ ํƒ ์‚ฌํ•ญ์œผ๋กœ ํ‘œ์‹œํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

interface Props {
    name: string;
    optional?: string;
}

๋ฆด๋ฆฌ์Šค ๋…ธํŠธ์˜ ๋‚ด์šฉ๊ณผ ์ผ์น˜ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

export interface Props {
    name: string;
}

// ...

function Greet({ name = "world" }: Props) {
    return <div>Hello {name.toUpperCase()}!</div>;
}

prop์„ ์„ ํƒ์ ์œผ๋กœ ๋งŒ๋“œ๋Š” ๊ฒƒ์€ ์ปดํฌ๋„ŒํŠธ ์™ธ๋ถ€์—์„œ (ํ…Œ์ŠคํŠธ์—์„œ) props์— ์ ‘๊ทผ ํ•  ๋•Œ ์œ ํ˜• ์‹œ์Šคํ…œ์ด prop์ด ํ•ญ์ƒ ์กด์žฌํ•œ๋‹ค๊ณ  ๋ณด์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ:

const wrapper = shallow(<Test/>);

// optional is string | undefined instead of string
const optional = wrapper
        .find(Component)
        .props()
        .optional;

optional ๊ฐ€ ํ•จ์ˆ˜ ์˜€๋‹ค๋ฉด ์‹คํ–‰ํ•˜๊ธฐ ์ „์— ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด preventDefault ๋งŒ ํ˜ธ์ถœํ•˜๋Š” ๊ธฐ๋ณธ onClick prop์„ ์ œ๊ณตํ•˜๋Š” ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค.

TypeScript 3.1 ์ด์ƒ์—์„œ๋Š” ๋‹ค์Œ์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค.

export interface Props {
    name: string;
}

function Greet(props: Props) {
    return <div>Hello {props.name.toUpperCase()}!</div>;
}
Greet.defaultProps = {
  name: "world",
} as Partial<Props>; // Good practice, without it you could use a number as default prop and TypeScript wouldn't complainโ€ฆ

const component = <Greet />; // no errors

๊ธฐ๋ณธ ์ด๋‹ˆ์…œ ๋ผ์ด์ € ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋ฉด ๊ธฐ๋ณธ ์†Œํ’ˆ์—์„œ ์ž‘๋™ํ•˜์ง€ ์•Š๋Š” ๋ฒ„๊ทธ์ž…๋‹ˆ๊นŒ? ๋ฆด๋ฆฌ์Šค ๋…ธํŠธ์— ์ œ๊ณต๋œ ์˜ˆ์ œ๊ฐ€ ์ตœ์‹  TypeScript ๋ฒ„์ „์—์„œ ์ž‘๋™ํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์ ์„ ๊ฐ์•ˆํ•  ๋•Œ ํšŒ๊ท€์ฒ˜๋Ÿผ ๋ณด์ž…๋‹ˆ๋‹ค.

@tbassetto ๋ฌธ์ œ๋Š” TS๊ฐ€ ์‹ค์ œ๋กœ ๊ธฐ๋ณธ๊ฐ’์„ ์ œ๊ณตํ•˜๋„๋ก ๊ฐ•์š”ํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

import React from 'react';

interface Props {
  myProp: string;
}

const Comp = (props: Props) => {
  return (
    <div>
      {props.myProp.toUpperCase()}
    </div>
  );
};

Comp.defaultProps = {

} as Pick<Props, 'myProp'>;

const comp = <Comp />;

๋˜ ๋‹ค๋ฅธ ์„ฑ๊ฐ€์‹  ์ ์€ ์ฒ˜์Œ์—์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ด์•ผํ•œ๋‹ค๋Š” ์‚ฌ์‹ค์ž…๋‹ˆ๋‹ค. Comp ๋ฅผ React.FC<Props> ๋กœ ์ž…๋ ฅํ•˜๋ฉด defaultProps ๋Š” ์ž˜ ์ž…๋ ฅ๋˜์ง€๋งŒ ๋‹ค๋ฅธ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. .

๋Œ€์ฒด๋กœ ํ˜„์žฌ TypeScripts JSX / React ์ง€์›์€ ์ด์ƒํ•˜๊ฒŒ ์™„๋ฒฝํ•˜์ง€ ์•Š์€ "์ƒํƒœ ์ธ ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด์ง€๋งŒ ๋ชจ๋“  ๊ฒƒ์ด ์ •์ƒ์ธ ๊ฒƒ์ฒ˜๋Ÿผ ์‹คํ–‰ ์ค‘์ž…๋‹ˆ๋‹ค (์ ์–ด๋„ ๋‚ด ๋ˆˆ์—๋Š”-์ฃผ๋กœ ์†”๋ฆฌ๋“œ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. 3.4.5์— ๋Œ€ํ•œ ์œ ํ˜• ์•ˆ์ „ ๋ฐฉ์‹์œผ๋กœ ์ƒํƒœ ๋น„ ์ €์žฅ React ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” "๊ณต์‹"๋ฐฉ์‹์ด ๋ฌด์—‡์ธ์ง€์— ๋Œ€ํ•œ ์ฐธ๊ณ  ์‚ฌํ•ญ).

๋ชจ๋“  ์‚ฌ๋žŒ๋“ค์ด ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์„ ์–ธ๊ธ‰ํ•˜๋Š” ๊ฒƒ์„ ๋ณด์•˜์ง€๋งŒ ๊ทธ๋“ค ์ค‘ ์–ด๋Š ๊ฒƒ๋„ ์ž‘๋™ํ•˜๊ฒŒ ํ•  ์ˆ˜๋Š” ์—†์Šต๋‹ˆ๋‹ค.

๋ฒ„์ „ 3.3.3์ž…๋‹ˆ๋‹ค.

export interface Props {
  shouldTruncateContent: boolean;
}

const Alert: React.FunctionComponent<Props> = ({
  children,
  shouldTruncateContent = false
}) => {
  return (
        <S.Content shouldTruncateContent={shouldTruncateContent} size="fill">
          {children}
        </S.Content>
  );
};

Alert.defaultProps = {
  shouldTruncateContent: false
} as Pick<Props, 'shouldTruncateContent'>;

export default Alert;

์™œ ์ด๊ฒƒ์ด ์—ฌ์ „ํžˆ ์˜ค๋ฅ˜๋ฅผ ๋˜์ง€๋Š” ์ง€์— ๋Œ€ํ•œ ํ†ต์ฐฐ๋ ฅ์ด ์žˆ์Šต๋‹ˆ๊นŒ?

TS2741: Property 'shouldTruncateContent' is missing in type '{ children: string; }' but required in type 'Props'.

๋‚˜๋Š” # 27425์™€ # 519์—์„œ ์ œ์•ˆ ๋œ ๋ชจ๋“  ์ˆ˜์ •์„ ์‹œ๋„ํ–ˆ๋‹ค.

@tomspeak ๋‚ด ํ•ด๊ฒฐ ๋ฐฉ๋ฒ• ๋˜๋Š” @Hotell ์˜ woraround (๋ชจ๋“  ์†Œํ’ˆ์„ ์„ ํƒ ์‚ฌํ•ญ์œผ๋กœ ๋งŒ๋“ค์ง€ ๋งŒ) ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค. ์ƒ˜ํ”Œ ์ฝ”๋“œ๋กœ 3.5์—์„œ ํ…Œ์ŠคํŠธ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๋ชจ๋“  ์‚ฌ๋žŒ๋“ค์ด ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์„ ์–ธ๊ธ‰ํ•˜๋Š” ๊ฒƒ์„ ๋ณด์•˜์ง€๋งŒ ๊ทธ๋“ค ์ค‘ ์–ด๋Š ๊ฒƒ๋„ ์ž‘๋™ํ•˜๊ฒŒ ํ•  ์ˆ˜๋Š” ์—†์Šต๋‹ˆ๋‹ค.

๋ฒ„์ „ 3.3.3์ž…๋‹ˆ๋‹ค.

export interface Props {
  shouldTruncateContent: boolean;
}

const Alert: React.FunctionComponent<Props> = ({
  children,
  shouldTruncateContent = false
}) => {
  return (
        <S.Content shouldTruncateContent={shouldTruncateContent} size="fill">
          {children}
        </S.Content>
  );
};

Alert.defaultProps = {
  shouldTruncateContent: false
} as Pick<Props, 'shouldTruncateContent'>;

export default Alert;

์™œ ์ด๊ฒƒ์ด ์—ฌ์ „ํžˆ ์˜ค๋ฅ˜๋ฅผ ๋˜์ง€๋Š” ์ง€์— ๋Œ€ํ•œ ํ†ต์ฐฐ๋ ฅ์ด ์žˆ์Šต๋‹ˆ๊นŒ?

TS2741: Property 'shouldTruncateContent' is missing in type '{ children: string; }' but required in type 'Props'.

๋‚˜๋Š” # 27425์™€ # 519์—์„œ ์ œ์•ˆ ๋œ ๋ชจ๋“  ์ˆ˜์ •์„ ์‹œ๋„ํ–ˆ๋‹ค.

shouldTruncateContent : boolean; ์„ ํƒ์  shouldTruncateContent?: boolean;

์ด์— ๋Œ€ํ•œ ์„ค๋ช…์„œ๊ฐ€ ๋ช…ํ™•ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์ง€๋งŒ Props ์ธํ„ฐํŽ˜์ด์Šค์—์„œ ์†์„ฑ์„ ์„ ํƒ ์‚ฌํ•ญ์œผ๋กœ ํ‘œ์‹œํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

interface Props {
    name: string;
    optional?: string;
}

์ด ์˜ต์…˜ ์œ ํ˜•์„ ๋ชจ๋“  ๊ณณ์—์„œ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์™œ ๋ฐ˜๋Œ€ํ‘œ? ๋ˆ„๊ตฐ๊ฐ€ ์„ค๋ช… ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

@ralexhassle , ๋‚˜๋Š” ์•„๋ž˜ ํ‘œ๋ฅผ ์„ค๋ช… ํ•  ์ˆ˜ ์—†์ง€๋งŒ ์ด๊ฒƒ์ด ๊ตฌ์„ฑ ์š”์†Œ์˜ ์„ ํƒ์  ์†์„ฑ์„ ์ž…๋ ฅํ•˜๋Š” ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉ๋ฒ•์ž„์„ ์„ค๋ช… ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
์งˆ๋ฌธ์˜ ์˜ˆ์—์„œ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” defaultProps ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ์„ ํ˜ธ ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ๋‚ด๊ฐ€ ์•„๋Š” ํ•œ ์„ ํƒ์  ์†์„ฑ์„ ์„ ํƒ ์‚ฌํ•ญ์œผ๋กœ ํ‘œ์‹œํ•˜๋Š” ๊ฒƒ์€ ์•„๋งˆ๋„ ์™„๋ฒฝํ•˜๊ฒŒ ๊ดœ์ฐฎ์Šต๋‹ˆ๋‹ค ...

๋ฌธ์ œ์˜ ๋ชฉ์ ์€ props๊ฐ€ ์„ ํƒ ์‚ฌํ•ญ์ด ์•„๋‹ˆ์ง€๋งŒ ๊ธฐ๋ณธ๊ฐ’์ด ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค (๋”ฐ๋ผ์„œ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์•Œ ์ˆ˜ ์—†์Œ). ? ๋กœ prop์„ ์„ ํƒ์ ์œผ๋กœ ํ‘œ์‹œํ•˜๋ฉด TS๋Š” ์ •์˜๋˜์ง€ ์•Š์€ ๊ฒƒ์œผ๋กœ ๊ฐ„์ฃผํ•˜๊ณ  ํ•ด๋‹น ์ผ€์ด์Šค๋ฅผ ์ฒ˜๋ฆฌํ•˜๋„๋กํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ธฐ๋ณธ๊ฐ’์ด ์žˆ์œผ๋ฏ€๋กœ ์ •์˜๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ฐ˜๋Œ€ ํˆฌํ‘œ๋Š” ํ•ด์„ค์ž๊ฐ€ ๋ฌธ์ œ๋ฅผ ์ดํ•ดํ•˜์ง€ ๋ชปํ•˜๊ณ  3.0 ๋ฆด๋ฆฌ์Šค ๋…ธํŠธ์™€ ๋ชจ์ˆœ๋˜๋Š” ๊ฒƒ์„ ์ œ์•ˆํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

@RyanCavanaugh ๋ด‡์ด์ด ๋ฌธ์ œ๋ฅผ ๋‹ซ์€ ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

๋†“์นœ ๊ฒƒ์ผ ์ˆ˜ ์žˆ์ง€๋งŒ ๋งŽ์€ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ์‹ค์ œ ๋‹ต๋ณ€์„ ๋ณผ ์ˆ˜ ์—†์Šต๋‹ˆ๊นŒ?

์ถ”์‹  : TypeScript v4 ์‚ฌ์šฉ

์ด๋Ÿฐ ์‹์œผ๋กœ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ Props์˜ ์†์„ฑ์€ ์„ ํƒ์  ๋งค๊ฐœ ๋ณ€์ˆ˜์ด๋ฉฐ ๊ตฌ์„ฑ ์š”์†Œ์—์„œ ์ถ”๊ฐ€๋กœ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

interface Props {
  children: ReactNode;
  properties?: Record<string, unknown>;
}

const defaultProps: Props = {
  children: '',
  properties: {
    marginTop: 32,
    marginLeft: 0,
    marginBottom: 8,
    marginRight: 0,
  },
};

const RadioListLabel: React.FC<Props> = ({
  children,
  properties = defaultProps.properties,
}) => (
  <View
    margin={[
properties ? properties.marginTop : 0
        properties ? properties.marginLeft : 0
        properties ? properties.marginBottom : 0
        properties ? properties.marginRight : 0
      ]}
  >
    <Text size="m" variant="heading2" lineHeight="body">
      {children}
    </Text>
  </View>
  )

@lakhmeranikita ์ด ๋ฌธ์ œ์˜ ๋ชฉ์ ์€ ์†Œํ’ˆ ์œ ํ˜•์„ ์„ ํƒ ์‚ฌํ•ญ์œผ๋กœ ํ‘œ์‹œํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

ํ•จ์ˆ˜ ๊ธฐ๋ณธ ์ธ์ˆ˜ ๋˜๋Š” ๋‹ค๋ฅธ ์ œ์•ˆ์„ ์ œ๋Œ€๋กœ ์ž…๋ ฅํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ์ฐพ์€ ์œ ์ผํ•œ ํ•ด๊ฒฐ์ฑ…์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

import React from "react";

type DefaultProps = { optionalProp: number };
type Props = {
  requiredProp: "some_string" | "other_string";
} & DefaultProps;

export const BaseComponent = (props: Props) => {
  const { requiredProp, optionalProp } = props;
  return (
    <div>
      {requiredProp} {optionalProp}
    </div>
  );
};

BaseComponent.defaultProps = {
  optionalProp: 0,
} as Partial<DefaultProps>;

const comp = <BaseComponent requiredProp="some_string" />;

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