일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
- Apollo Server
- redux saga
- task-definition
- Apollo Client
- Spa
- NextJS
- restore scroll position
- Babel
- nvm
- 스코프체이닝
- graphql
- 비동기 처리
- SSR
- 환경 레코드
- 식별자 결정
- 세션스토리지
- 타입스크립트
- Webpack
- 실행 컨텍스트
- SAGA 패턴
- CSR
- Architecture
- 마이크로 프론트엔드
- 무한 스크롤
- Next.js
- useRef
- ESLint
- Typescript
- 변수 섀도잉
- scrollTo 안됨
- Today
- Total
minguri brain is busy
TypeScript 5.1 베타 발표 살펴보기 본문
이글은 Announcing TypeScript 5.1 Beta 글을 번역, 재정리한 글입니다.
4월 18일 TypeScript 5.1 의 베타 릴리스를 발표했습니다.
아래의 명령어로 베타 사용을 시작할 수 있습니다.
$ npm install -D typescript@beta
TypeScript 5.1의 새로운 기능 목록 한눈에 보기
- Undefined를 리턴하는 함수에 대해 더 쉬운 암시적 리턴
- Getter와 Setter에 관련 없는 타입
- JSX 요소와 JSX 태그 타입 간 분리된 타입 체크
- 네임스페이스 JSX 속성
- typeRoots는 Module Resolution에서 참조
- JSX 태그용 링크된 커서
- @param JSDoc 태그에 대한 스니펫 완성
- 최적화
- 브레이킹 체인지
1. Undefined를 리턴하는 함수에 대해 더 쉬운 암시적 리턴이 가능합니다.
자바스크립트에서는 함수가 return 을 치지 않고 실행하면 undefined 를 리턴합니다.
funtion foo() {
// no return
}
// x = undefined
let x = foo();
반면 타입스크립트의 이전버전에서는 return 문이 전혀 없을 수 있는 유일한 함수는 void-, any- 리턴 함수 였습니다. 즉, "이 함수는 undefined를 리턴합니다"라고 명시적으로 말하더라도 최소한 하나의 return 문이 있어야 합니다.
// ✅ 가능 - 'f1'이 void를 반환한다고 추론했습니다.
function f1() {
// no returns
}
// ✅ 가능 - 'void'는 return문이 필요하지 않습니다.
function f2(): void {
// no returns
}
// ✅ 가능 - 'any'는 return문이 필요하지 않습니다.
function f3(): any {
// no returns
}
// ❌ 에러!
// 선언된 타입이 'void'도 'any'도 아닌 함수는 값을 return 해야 합니다.
function f4(): undefined {
// no returns
}
어떤 API가 undefined를 반환하는 함수를 기대할 경우 이것은 문제가 될 수 있습니다. 적어도 하나의 명시적 undefined 리턴 또는 return 문과 명시적 주석이 있어야 합니다.
declare function takesFunction(f: () => fundefined): undefined;
// ❌ 에러!
// '() => void'타입의 인수는 '() => undefined'유형의 파라미터에 할당할 수 없습니다.
takesFunction (() => {
// no returns
});
// ❌ 에러!
// 선언된 타입이 'void'도 'any'도 아닌 함수는 값을 리턴해야 합니다.
takesFunction ((): undefined => {
// no returns
});
// ❌ 에러!
// '() => void' 타입의 인수는 '() => undefinded' 타입의 파라미터에 할당할 수 없습니다.
takesFunction ((): undefined => {
return;
});
// ✅ 작동
takesFunction(() => {
return undefined;
});
// ✅ 작동
takesFunction((): undefined => {
return undefined;
});
이 동작은 특히 제어할 수 없는 함수를 호출할 때 실망스럽고 혼란스러웠습니다.
첫째로 타입스크립트 5.1은 이제 undefined 리턴 함수에 리턴문이 없는 것을 허용합니다.
// ✅ 타입스크립트5.1에서 동작!
function f4(): undefined {
// no returns
}
// ✅ 타입스크립트5.1에서 동작!
takeFunction((): undefined => {
// no returns
});
둘째로, 함수에 리턴 표현식이 없고 undefined 리턴을 기대하는 함수로 전달되는 경우 타입스크립는 해당 함수의 리턴 타입을 undefined으로 추론합니다.
// ✅ 타입스크립트5.1에서 동작!
takesFunction(function f() {
// 리턴 타입이 undefined
// no returns
});
// ✅ 타입스크립트5.1에서 동작!
takesFunction(function f() {
// 리턴 타입이 undefinded
return;
});
또 다른 유사한 문제점을 해결하기 위해 타입스크립트의 --noImplicitReturns 옵션에서 undefined만 리턴하는 함수는 이제 모든 단일 모드 패스가 명시적 리턴으로 끝나야 하는 것이 아니라는 점에서 void 와 유사한 예외를 갖습니다.
// ✅ 타입스크립트5.1에서 '--noImplicitReturns'로 동작!
function f(): undefined {
if (Math.random()) {
// ...
return;
}
}
자세한 내용은 원래 이슈와 구현 풀리퀘에서 읽을 수 있습니다.
2. Getter와 Setter에 관련 없는 타입
타입스크립트 4.3에서는 get 및 set 접근자가 두가지 다른 타입을 지정할 수 있다고 할 수 있습니다.
interface Serializer {
set value(v: string | number | boolean);
get value(): string;
}
declare let box: Serializer;
// 'boolean' 허용
box.value = true;
// 'string'으로 출력
console.log(box.value.toUpperCase());
처음에 우리는 get 타입이 set 타입의 하위 유형이어야 한다고 요구했습니다. 이것은
box.vlaue = box.vlaue;
로 쓰는 것이 항상 유효하다는 것을 의미했습니다.
그러나 기존 API와 제안된 API 중에는 getter 와 setter 사이에 완전히 관련이 없는 타입이 많이 있습니다. 예를들어 가장 일반적인 예 중 하나인 DOM 및 CSSStyleRule API의 스타일 속성을 생각해보세요. 모든 스타일 규칙에는 CSSStyleDeclaration인 스타일 속성이 있습니다. 그러나 해당 속성에 쓰려고 하면 string만 올바르게 작동합니다!
타입스크립트 5.1은 명시적인 타입 주석이 있는 경우 get 및 set 접근자 속성에 대해 완전히 관련 없는 타입을 허용합니다. 이 버전의 타입스크립트는 아직 이러나 내장 인터페이스의 타입을 변경하지 않지만 이제 CSSStyleRule을 다음과 같은 방식으로 정의할 수 있습니다.
interface CSSStyleRule {
// ...
/** 항상 `CSSStyleDeclaration`로 읽습니다. */
get style(): CSSStyleDeclaration;
/** 여기에는 `string`만 쓸 수 있습니다. */
set style(newValue: string);
// ...
}
이것은 또한 set 접근자가 "유효한" 데이터만 수락하도록 요구하는 것과 같은 또다른 패턴을 허용하지만 일부 기본 상태가 아직 초기화되지 않은 경우 get 접근자가 undefined를 리턴할 수 있도록 지정합니다.
class SafeBox {
#value: string | undefined
// string만 허용된다.
set value(newValue: string) {
}
// 반드시 'undefined'를 체크하세요!
get value(): string | undefined {
return this.#value;
}
}
실제로 이것은 --exactOptionalProperties 에서 선택적 속성을 확인하는 방법과 유사합니다.
이 내용은 구현 풀리퀘에서 더 자세히 알아볼 수 있습니다.
3. JSX 요소와 JSX 태그 타입 간 분리된 타입 체크
타입스크립트가 JSX와 관련하여 가지고 있던 한가지 문제점은 모든 JSX 요소의 태그 타입에 대한 요구사항 이었습니다.
컨텍스트에서 JSX 요소는 다음 중 하나입니다.
// self-closing JSX 태그
<Foo />
// 열기/닫기 태그를 가진 일반적 요소
<Bar></Bar>
<Foo /> 또는 <Bar></Bar> 타입을 체크할 때 타입스크립트는 항상 JSX라는 네임스페이스를 조회하고 Element라는 유형을 가져오거나 더 직접적으로 JSX.Element를 조회합니다.
그러나 타입스크립트는 Foo 또는 Bar 자체가 태그 이름으로 사용할 수 있는지 여부를 확인하기 위해 Foo 또는 Bar 에서 리턴하거나 생성한 형식을 대략적으로 파악하고 JSX.Element(또는 타입이 구성 가능한 경우 JSX.ElementClass 라고 하는 다른 타입)와의 호환성을 확인합니다.
여기서 제한 사항은 구성요소가 JSX.Element보다 더 광범위한 타입을 반환하거나 "렌더링"한 경우에는 사용할 수 없다는 것을 의미했습니다. 예를들어 JSX 라이브러리가는 string이나 Promise를 반환하는 구성 요소로도 괜찮을 수 있습니다
좀 더 구체적인 예로, React는 Promise를 리턴하는 구성 요소에 대한 제한된 지원을 추가하는 것을 고려하고 있지만 기존 버전의 타입스크립트는 누군가 JSX.Element의 타입을 대폭 완화하지 않고는 이를 표현할 수 없습니다.
import * as React from "react";
async function Foo() {
return <div></div>;
}
let element = <Foo />;
// 'Foo'는 JSX 컴포넌트로 사용될 수 없습니다.
// 리턴타입 'Promise<Element>'는 유효한 JSX 요소가 아닙니다.
이를 표현하는 방법을 라이브러리에 제공하기 위해 타입스트립트 5.1은 이제 JSX.ElementType이라는 타입을 찾습니다. ElementType은 JSX 요소에서 태그로 사용하기에 유효한 항목을 정확하게 지정합니다. 따라서 이제는 다음과 같이 입력할 수 있습니다.
namespace JSX {
export type ElementType =
// 유효한 모든 소문자 태그들
keyof IntrinsicAttributes
// 함수형 컴포넌트
(props: any) => Element
// 클래스 컴포넌트
new (props: any) => ElementClass;
export interface IntrinsictAttributes extends /*...*/ {}
export type Element = /*...*/;
export type ClassElement = /*...*/;
}
4. 네임스페이스 JSX 속성
타입스크립트는 이제 JSX를 사용할 때 네임스페이스 속성 이름을 지원합니다.
import * as React from "react";
// 둘 다 동일합니다.
const x = <Foo a:b="hello" />;
const y = <Foo a : b="hello" />;
interface FooProps {
"a:b": string;
}
function Foo(props: FooProps) {
return <div>{props["a:b"]}</div>;
}
네임스페이스 태그 이름은 이름의 첫번째 세그먼트가 소문자 이름일 때 JSX.IntrinsicAttributes에서 유사한 방식으로 조회됩니다.
// 일부 라이브러리 코드 또는 해당 라이브러리의 확장에서:
namespace JSX {
interface IntrinsicElements {
["a:b"]: { props: string };
}
}
// 우리의 코드에서:
let x = <a:b props="hello!" />;
5. typeRoots는 Module Resolution에서 참조됩니다.
타입스트크립트의 지정돈 모듈 조회 전략이 경로를 확인할 수 없는 경우 이제 지정된 typeRoots에 상대적인 패키지를 확인합니다.
자세한 내용은 이 풀리퀘를 참조하세요.
6. JSX 태그용 링크된 커서
타입스크립트는 이제 JSX 태그 이름에 대한 링크 편집을 지원합니다. 링크된 편집("미러커러"라고도 함)을 사용하면 편집자가 여러 위치를 동시에 자동으로 편집할 수 있습니다.
이 새로운 기능은 타입스크립트 및 자바스크립트 파일 모두에서 작동해야하며 VScode Insider에서 활성화할 수 있습니다. VScode에서는 설정에서 Editor: Linked Editing 옵션을 편집할 수 있습니다.
또는 JSON 설정 파일에서 editor.linkedEditing을 구성합니다.
{
// ...
"editor.linkedEditing": true,
}
이 기능은 Visual Studio 17.7 Preview 1에서도 지원됩니다.
여기에서 링크된 편집의 구현을 볼 수 있습니다.
7. @param JSDoc 태그에 대한 스니펫 완성
타입스크립트는 이제 타입스크립트 및 자바스크립트 파일 모두에서 @param 태그를 입력할 때 스니펫 완성을 제공합니다. 이렇게 하면 코드를 문서화하거나 자바스크립트에서 JSDoc 타일을 추가할 때 일부 타이핑 및 텍스트 이동을 줄이는 데 도움이 될 수 있습니다.
이 새로운 기능이 어떻게 구현되었는지 Github에서 확인할 수 있습니다.
8. 최적화
1) 불필요한 타입 인스턴스화 방지
타입스크립트 5.1은 이제 외부 타입 매개변수에 대한 참조를 포함하지 않는 것으로 알려진 객체 타입 내에서 타입 인스턴스화를 수행하지 않습니다. 이는 많은 불필요한 계산을 줄일 수 있는 잠재력이 있으며 material-ui 문서 디렉토리의 유형 검사 시간을 50% 이상 줄였습니다.
GitHub에서 이 변경 사항과 관련된 변경 사항을 확인할 수 있습니다.
2) 유니언 리터럴에 대한 부정 케이스 검사
소스 타입이 유니언 타입의 일부인지 확인할 때 타입스크립트는 먼저 해당 소스에 대한 내부 타입 식별자를 사용하여 빠른 조회를 수행합니다. 해당 조회가 실패하면 타입스크립트는 유니언 내의 모든 타입에 대한 호환성을 확인합니다.
리터럴 타입을 순수 리터럴 타입의 합집합에 연결할 때 타입스크립트는 이제 유니언의 다른 모든 타입에 대하 전체 작업을 피할 수 있습니다. 이 가정은 타입스크립트가 항상 리터럴 타입을 인턴/캐시하기 때문에 안전하지만 "프레쉬한" 리터럴 유형과 관련하여 처리해야할 몇가지 극단적인 경우가 있습니다.
이 최적화를 통해 이번 이슈에서 코드의 타입 체크 시간을 약 45초에서 약 0.4초로 줄일 수 있었습니다.
3) JSDoc 구문 분석을 위한 스캐너 호출 감소
이번 버전의 타입스크립트는 JSDoc 주석을 구문 분석 할 때 스캐너/토크나이저를 사용하여 주석을 세분화된 토큰으로 나누고 내용을 다시 조각냅니다. 이는 여러 공백이 하나로 줄어들도록 주석 텍스트를 정규화하는데 도움이 될 수 있습니다. 그러나 그것은 매우 "수다스럽고" parser와 scanner가 매우 자주 앞뒤로 이동하여 JSDoc 구문 분석에 오버헤드를 준다는 것을 의미했습니다.
타입스크립트 5.1은 JSDoc 주석을 스캐너/토크나이저로 분해하는 로직을 더 많이 넣었습니다. 이제 스캐너는 필요에 따라 더 큰 콘텐츠 청크를 parser에 직접 리턴합니다.
이러한 변경으로 인해 mostly-prose-comment 자바스크립트 파일의 10Mb 구문 분석 시간이 약 절반으로 단축되었스빈다. 좀 더 현실적인 예를 들어, 당사의 성능 제품군의 xstate 스냅샷은 약 300ms의 구문 분석 시간을 단축하여 로드 및 분석 속도를 높였습니다.
9. 브레이킹 체인지
1) ES2020 및 Node.js 14.17의 최소 런타임 요구 사항
타입스크립트 5.1은 이제 ECMAScript 2020에 도입된 자바스크립트 기능을 제공합니다. 결과적으로 최소한 타입스크립트는 상당히 현대적인 런타임에서 실행되어야 합니다. 대부분의 사용자에게는 이는 타입스크립트가 이제 Node.js 14.17 이상에서만 실행됨을 의미합니다.
Node 10 또는 12와 같은 이전 버전의 Node.js에서 타입스크립트 5.1을 실행하려고 하면 tsc.js 또는 tsserver.js 를 실행할 때 다음과 같은 오류가 표시될 수 있습니다.
node_modules/typescript/lib/tsserver.js:2406
for (let i = startIndex ?? 0; i < array.length; i++) {
^
SyntaxError: Unexpected token '?'
at wrapSafe (internal/modules/cjs/loader.js:915:16)
at Module._compile (internal/modules/cjs/loader.js:963:27)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)
at Module.load (internal/modules/cjs/loader.js:863:32)
at Function.Module._load (internal/modules/cjs/loader.js:708:14)
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:60:12)
at internal/main/run_main_module.js:17:47
또한 타입스크립트를 설치하려고 하면 npm에서 다음과 같은 오류 메시지가 표시됩니다.
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE package: 'typescript@5.1.0-beta',
npm WARN EBADENGINE required: { node: '>=14.17' },
npm WARN EBADENGINE current: { node: 'v12.22.12', npm: '8.19.2' }
npm WARN EBADENGINE }
yarn 의 경우:
error typescript@5.1.0-beta: The engine "node" is incompatible with this module. Expected version ">=14.17". Got "12.22.12"
error Found incompatible module.
이 변경 사항에 대한 자세한 내용은 여기를 참조하십시오.
2) 명시적 typeRoots는 node_modules/@types에 대한 위로 걷기를 비활성화합니다.
이전에는 typeRoots 옵션이 tsconfig.json에 지정되었지만 모든 유형의 Rootes 디렉토리에 대한 확인이 실패했을 때 타입스크립트는 각 부모의 node_modules/@types 폴더 내에서 패키지를 확인하려고 시도하면서 부모 디렉토리를 계속 검색했습니다.
이동작은 과도한 조회를 유발할 수 있으면 타입스크립트 5.1에서 비활성화 되었습니다. 결과적으로 tsconfig.json의 타입 옵션 또는 /// <reference > 지시문의 항목을 기반으로 다음과 같은 오류가 표시될 수 있습니다.
error TS2688: Cannot find type definition file for 'node'.
error TS2688: Cannot find type definition file for 'mocha'.
error TS2688: Cannot find type definition file for 'jasmine'.
error TS2688: Cannot find type definition file for 'chai-http'.
error TS2688: Cannot find type definition file for 'webpack-env"'.
솔루션은 일반적으로 node_modules/@types에 대한 특정 항목을 typeRoots에 추가하는 것입니다.
{
"compilerOptions": {
"types": [
"node",
"mocha"
],
"typeRoots": [
// 이전에 가지고 있던 것을 유지
"./some-custom-types/",
// 로컬 'node_modules/@types'가 필요할 수 있습니다.
"./node_modules/@types",
// 공유 'node_modules/@types'를 지정해야 할 수도 있습니다.
// "monorepo" 레이아웃을 사용하는 경우.
"../../node_modules/@types",
]
}
}
이슈트래커에서 자세한 내용을 확인할 수 있습니다.
타입스크립트 5.1은 앞으로 릴리즈 후보와 최종 안정적 릴리즈 개발을 진행할 예정이라고 합니다. 타입스크립 5.1의 목표 릴리즈 날짜들이 포함된 계획을 지켜보세요.
'FE > 개발' 카테고리의 다른 글
Apollo Server 4로 마이그레이션(sandbox 랜딩페이지 플러그인 설정, health check 적용) (0) | 2023.08.24 |
---|---|
웹페이지에 구조화된 데이터(JSON-LD) 추가하기 (0) | 2023.05.04 |
무한스크롤 페이지에서 스크롤 위치 복원 (0) | 2022.12.23 |
Next.js의 SSR, CSR 기본개념알아보기 (0) | 2022.08.04 |
Saga패턴 vs Redux-Saga / 간단한 예제로 살펴보기 (0) | 2022.07.22 |