Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2023-07-17 TypeScript 5.1 핥아보기 #34

Closed
pumpkiinbell opened this issue Jul 17, 2023 · 0 comments
Closed

2023-07-17 TypeScript 5.1 핥아보기 #34

pumpkiinbell opened this issue Jul 17, 2023 · 0 comments

Comments

@pumpkiinbell
Copy link
Member

[Documentation - TypeScript 5.1](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-1.html)

[Announcing TypeScript 5.1 - TypeScript](https://devblogs.microsoft.com/typescript/announcing-typescript-5-1/)


변경점

undefined 를 반환하는 함수를 위한 더 자연스러운 암시적인 반환

배경

  • JS 는 함수가 return 문 없이 종료되면 값을 undefined 로 반환합니다.

    function foo() {
        // no return
    }
    // x = undefined
    let x = foo();
  • 그러나 TS 는 “return 문이 없는 함수” 는 void 를 반환하도록 추론하며, 이를 제외하면 any 만 annotation 이 가능합니다.

    // ✅ fine - we inferred that 'f1' returns 'void'
    function f1() {
        // no returns
    }
    // ✅ fine - 'void' doesn't need a return statement
    function f2(): void {
        // no returns
    }
    // ✅ fine - 'any' doesn't need a return statement
    function f3(): any {
        // no returns
    }

문제점

  • 이 말은 즉슨, undefined 를 반환한다고 작성하더라도 오류가 난다는 것입니다.

    // ❌ error!
    // A function whose declared type is neither 'void' nor 'any' must return a value.
    function f4(): undefined {
        // no returns
    }
    • ☝🏻 이 케이스에서 오류가 발생하지 않기 위해선 반드시 하나의 return 문이 존재해야 합니다.
  • 특히, 아래와 같이 undefined 를 반환하는 함수를 인자로 기대하는 경우 문제가 되는데요.

    declare function takesFunction(f: () => undefined): undefined;
    • 이런 경우, undefined 를 명시적으로 반환하거나

      // ✅ works
      takesFunction(() => {
          return undefined;
      });
    • return 문 + 명시적인 annotation 을 해줘야 합니다.

      // ✅ works
      takesFunction((): undefined => {
          return;
      });
    • 이런 경우는 오류로 취급합니다.

      // ❌ error!
      // '() => void' 는 '() => undefined' 에 할당할 수 없습니다.
      takesFunction(() => {
          // no returns
      });
      // ❌ error!
      // 함수가 void 나 any 를 받환하도록 선언되지 않는다면, value 를 무조건 return 해야 합니다.
      takesFunction((): undefined => {
          // no returns
      });
      // ❌ error!
      // '() => void' 는 '() => undefined' 에 할당할 수 없습니다.
      takesFunction(() => {
          return;
      });

TS 5.1

TS 5.1 부턴 다음과 같이 취급합니다.

  1. return 문이 없는 함수도 undefined 를 반환하도록 선언할 수 있습니다.

    // ✅ Works in TypeScript 5.1!
    function f4(): undefined {
        // no returns
    }
    // ✅ Works in TypeScript 5.1!
    takesFunction((): undefined => {
        // no returns
    });
  2. 만약, 함수가 return 문이 없고 undefined 를 반환하는 함수를 기대하는 곳의 인자로 넘겨진다면, undefined 를 반환하는 함수라고 추론합니다.

    // ✅ Works in TypeScript 5.1!
    takesFunction(function f() {
        //                 ^ return type is undefined
        // no returns
    });
    // ✅ Works in TypeScript 5.1!
    takesFunction(function f() {
        //                 ^ return type is undefined
        return;
    });

Getter 의 반환 타입과 Setter 인자 타입의 포함 관계 제거

배경

문제점

TS 5.1

TS 5.1 은 get, set 접근자 속성이 불일치하는 타입일지라도 이를 허용해줍니다.

  • 앞서 언급한 예시 같은 경우, 다음과 같이 작성하는 방식으로 해결 가능합니다.

    interface CSSStyleRule {
        // ...
        /** Always reads as a `CSSStyleDeclaration` */
        get style(): CSSStyleDeclaration;
        /** Can only write a `string` here. */
        set style(newValue: string);
        // ...
    }
  • 이는 다음과 같은 패턴도 허용합니다.

    • 오로지 유효한 데이터만 받는 set 접근자를 필수적으로 요구하지만,
    • 아직 초기화되지 않은 경우를 위해 undefined 를 반환하는 get 접근자

JSX Element 와 Tag Type 간의 타입 체킹 디커플링

배경

<  Component />
// ^^^^^^^^^ JSX element type
//^^^^^^^^^^^^^ JSX element

<Foo />
<Bar></Bar>
  • TS 는 와 의 타입을 검사할 때 다음과 같은 방식을 사용합니다.
    • JSX 라고 불리는 namespace 를 탐색합니다.
    • 이 안에 있는 Element 라는 타입을 가져옵니다. 즉, JSX.Element 를 바라봅니다.
  • Foo, Bar 가 유효한 태그 이름인지 알기 위하여 다음과 같은 방식을 사용합니다.
    • TS 는 명시적으로 annotation 한 반환 타입을 가져옵니다.
    • 만약 없다면, Foo, Bar 을 통해 추론한 타입을 가져와 JSX.Element 와의 호환성을 검사합니다.

문제점

  • 이러한 면의 문제점은 컴포넌트가 JSX.Element 보다 더 넓은 범위의 타입을 반환하거나, render 되어질 경우 올바르게 동작하지 못한다는 점입니다.
    • 특히 React 는 Server Component 를 도입할 예정이 있는데, 이 경우 Promise 를 반환하게 됩니다.
      그러나 TS 는 JSX.Element 를 참조하게 되므로, 이를 올바르게 표현할 수 없습니다.

      async function Foo({id, isEditing}) {
        const note = await db.posts.get(id);
        return (
          <div>
            <h1>{note.title}</h1>
            <section>{note.body}</section>
            {isEditing ? <NoteEditor note={note} /> : null}
          </div>
        );
      }
      
      let element = <Foo />;
      //             ~~~
      // 'Foo' cannot be used as a JSX component.
      //   Its return type 'Promise<Element>' is not a valid JSX element

TS 5.1

TS 5.1 에선 이를 표현하기 위해, JSX.ElementType 이란 타입을 추가합니다.

  • 유효한 태그 이름인지 찾기 위해 ElementType 을 바라보게 됩니다.

  • ElementType 은 JSX element 내의 유효한 태그가 무엇인지 기재되어 있습니다.

    namespace JSX {
        export type ElementType =
            // All the valid lowercase tags
            keyof IntrinsicAttributes
            // Function components
            (props: any) => Element
            // Class components
            new (props: any) => ElementClass;
        export interface IntrinsictAttributes extends /*...*/ {}
        export type Element = /*...*/;
        export type ClassElement = /*...*/;
    }

ㅎㅎ

Untitled

[berry/CHANGELOG.md at master · yarnpkg/berry](https://github.com/yarnpkg/berry/blob/master/CHANGELOG.md#340)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant