분류 javascript

TypeScript의 유형 표기법 이해

컨텐츠 정보

  • 조회 297 (작성일 )

본문

이 블로그 게시물은 정적 유형에 대한 TypeScript 표기법에 대한 빠른 소개입니다.


https://2ality.com/2018/04/type-notation-typescript.html 


1. 배우는 것


이 게시물을 읽은 후 다음 코드의 의미를 이해할 수 있어야 합니다.


interface Array<T> {
  concat(...items: Array<T[] | T>): T[];
  reduce<U>(
    callback: (state: U, element: T, index: number, array: T[]) => U,
    firstState?: U): U;
  ···
}

이것이 암호화되어 있다고 생각되면 – 나는 당신에게 동의합니다. 그러나 (증명하기를 원할 때)이 표기법은 상대적으로 배우기 쉽습니다. 그리고 일단 이해하면 코드 작동 방식에 대한 즉각적이고 정확하며 포괄적 인 요약을 제공합니다. 영어로 긴 설명을 읽을 필요가 없습니다.


2. 코드 예제 사용해보기


TypeScript에는 온라인 놀이터가 있습니다. 가장 포괄적 인 점검을 받으려면“옵션”메뉴에서 모든 항목을 켜야 합니다. 이것은 --strict 모드에서 TypeScript 컴파일러를 실행하는 것과 같습니다.


3. 유형 검사의 종합성 지정


저는 항상 가장 포괄적인 설정 인 TypeScript를 엄격하게 사용합니다. 그것 없이는 프로그램 작성이 약간 쉬워 지지만 정적 유형 검사의 많은 이점을 잃게 됩니다. 현재 이 설정은 다음과 같은 하위 설정을 활성화합니다.


  • --noImplicitAny: TypeScript가 유형을 유추 할 수 없는 경우 유형을 지정해야 합니다. 이것은 주로 기능 및 방법의 매개 변수에 적용됩니다.이 설정을 사용하면 주석을 달아야 합니다.
  • --noImplicitThis: 이 유형이 명확하지 않은 경우 불평하십시오.
  • --alwaysStrict: 가능할 때마다 JavaScript의 엄격 모드를 사용하십시오.
  • --strictNullChecks: null은 자체 형식 인 null 이외의 모든 형식의 일부가 아니며 허용되는 값인 경우 명시 적으로 언급해야 합니다.
  • --strictFunctionTypes: 기능 유형을 더 강력하게 검사합니다.
  • --strictPropertyInitialization: 속성 값을 정의 할 수 없으면 생성자에서 초기화해야 합니다.

추가 정보 : TypeScript 핸드북의 "컴파일러 옵션"장.


4. 유형


이 블로그 게시물에서 유형은 단순히 값 집합입니다. JavaScript 언어 (TypeScript가 아님)에는 7 가지 유형이 있습니다.

  • Undefined: 유일한 요소가 undefined 세트
  • Null: 유일한 요소가 null 인 세트
  • Boolean: 두 요소가 false이고 true 인 세트
  • Number: 모든 숫자의 집합
  • String: 모든 문자열 세트
  • Symbol: 모든 심볼 세트
  • Object: 모든 객체 세트 (함수와 배열 포함)

이러한 유형은 모두 동적이므로 런타임에 사용할 수 있습니다.


TypeScript는 JavaScript에 추가 계층 인 정적 유형을 제공합니다. 소스 코드를 컴파일하거나 유형을 확인할 때만 존재합니다. 각 저장 위치 (변수 또는 속성)에는 동적 값을 예측하는 정적 유형이 있습니다. 유형 검사는 이러한 예측이 이루어 지도록 합니다. 그리고 코드를 실행하지 않고 정적으로 확인할 수 있는 것이 많이 있습니다. 예를 들어, 함수 f (x)의 매개 변수 x에 정적 유형 번호가 있는 경우 매개 변수 'abc'에 잘못된 정적 유형이 있기 때문에 함수 호출 f ( 'abc')가 유효하지 않습니다.


5. 타입 주석


변수 이름 뒤의 콜론은 타입 주석을 시작합니다 : 콜론 뒤의 타입 서명은 변수가 가질 수 있는 값을 나타냅니다. 예를 들어, 다음 줄은 TypeScript에 x가 숫자 만 저장한다는 것을 알려줍니다.


let x: number;

undefined x로 초기화되는 것이 정적 유형을 위반하지 않는지 궁금 할 수 있습니다. TypeScript는 값을 할당하기 전에 x를 읽지 못하게 하여 이 문제를 해결합니다.


6. 타입 추론


TypeScript에서는 모든 저장 위치에 정적 유형이 있지만 항상 명시적으로 지정할 필요는 없습니다. TypeScript가 종종 유추 할 수 있습니다. 예를 들어, 다음과 같이 쓰는 경우 :


let x = 123;

그런 다음 TypeScript는 x에 정적 유형 number가 있다고 추론합니다.


7. 타입 설명


타입 주석의 콜론 뒤에 오는 것은 소위 타입 표현식입니다. 이것들은 단순에서 복합까지 다양하며 다음과 같이 생성됩니다.


기본 유형은 유효한 유형 표현식입니다.


JavaScript의 동적 유형에 대한 정적 유형 : 

  • undefined, null
  • boolean, number, string
  • symbol
  • object.
  • Note: value undefined vs. type undefined (depends on locations)

TypeScript 관련 유형 : 

  • Array (not technically a type in JS)
  • any (the type of all values)
  • Etc.

"값으로 undefined"및 "유형으로 undefined"은 모두 undefined으로 작성됩니다. 사용 위치에 따라 값 또는 유형으로 해석됩니다. null도 마찬가지입니다.


유형 연산자를 통해 기본 유형을 결합하여 더 많은 유형 표현식을 작성할 수 있습니다. 유형 연산자는 연산자 조합 (∪) 및 교차점 (∩)이 세트를 결합하는 방법과 유사하게 유형을 결합합니다.


다음 섹션에서는 TypeScript가 제공하는 몇 가지 유형 연산자에 대해 설명합니다.


8. 배열 유형


배열은 JavaScript에서 다음 두 가지 역할 (때로는 두 가지가 혼합 됨)에 사용됩니다.

  • Lists : 모든 요소의 유형이 동일합니다. 배열의 길이는 다양합니다.
  • Tuple : 배열의 길이가 고정되어 있습니다. 요소의 유형이 반드시 같을 필요는 없습니다.

8.1 lists의 배열 


배열 arr이 요소가 모두 숫자인 list로 사용된다는 사실을 표현하는 두 가지 방법이 있습니다.


let arr: number[] = [];
let arr: Array<number> = [];

일반적으로 TypeScript는 할당이 있는 경우 변수 유형을 유추 할 수 있습니다. 이 경우 빈 배열을 사용하면 요소 유형을 결정할 수 없으므로 실제로 도움이 되어야 합니다.


나중에 꺾쇠 괄호 표기법 (배열 <번호>)으로 돌아갑니다.


8.2 tuples로서의 배열


Array에 2 차원 점을 저장하면 해당 Array를 튜플로 사용합니다. 다음과 같이 보입니다.


let point: [number, number] = [7, 5];

이 경우 타입 주석이 필요하지 않습니다.


튜플의 또 다른 예는 Object.entries(obj)의 결과입니다. obj의 각 속성에 대해 하나의 [키, 값] 쌍을 가진 배열입니다.


> Object.entries({a:1, b:2})
[ [ 'a', 1 ], [ 'b', 2 ] ]

Object.entries()의 결과 유형은 다음과 같습니다.


Array<[string, any]>

9. 함수 유형


다음은 함수 유형의 예입니다.


(num: number) => string

이 유형은 단일 매개 변수, 숫자를 허용하고 문자열을 리턴하는 모든 함수로 구성됩니다. 이 유형을 유형 주석에 사용합시다 (여기서는 문자열이 함수로 사용됨).


const func: (num: number) => string = String;

TypeScript는 String 유형을 알고 있으므로 func 유형을 유추 할 수 있기 때문에 여기서는 일반적으로 유형 주석을 사용하지 않습니다.


다음 코드는 보다 현실적인 예입니다.


function stringify123(callback: (num: number) => string) {
  return callback(123);
}

stringify123()의 매개 변수 콜백을 설명하기 위해 함수 유형을 사용하고 있습니다. 이 형식 주석으로 인해 TypeScript는 다음 함수 호출을 거부합니다.


f(Number);

그러나 다음 함수 호출을 허용합니다.


f(String);

9.1 함수 선언의 결과 유형


함수의 모든 매개 변수에 주석을 다는 것이 좋습니다. 결과 유형을 지정할 수도 있습니다 (그러나 TypeScript는 이를 유추하는 데 능숙합니다).

function stringify123(callback: (num: number) => string): string {
  const num = 123;
  return callback(num);
}

특수 결과 유형 void 


void는 함수에 대한 특수 결과 유형입니다. TypeScript에 함수가 항상 정의되지 않은 (명시적으로 또는 암시적으로) 리턴한다고 알려줍니다.


function f1(): void { return undefined } // OK
function f2(): void { } // OK
function f3(): void { return 'abc' } // error

9.2 선택적 매개 변수


식별자 뒤에 물음표는 매개 변수가 선택 사항임을 의미합니다. 예를 들면 다음과 같습니다.


function stringify123(callback?: (num: number) => string) {
  const num = 123;
  if (callback) {
    return callback(num); // (A)
  }
  return String(num);
}

--strict 모드에서 TypeScript를 실행하는 경우 콜백이 생략되지 않았는지 미리 확인한 경우 A 행에서만 함수를 호출 할 수 있습니다.


Parameter default values 


TypeScript는 ES6 매개 변수 기본값을 지원합니다.


function createPoint(x=0, y=0) {
  return [x, y];
}

기본값은 매개 변수를 선택 사항으로 만듭니다. TypeScript가 형식을 유추 할 수 있으므로 일반적으로 형식 주석을 생략 할 수 있습니다. 예를 들어, x와 y 모두 유형 번호를 가지고 있다고 유추 할 수 있습니다.


유형 주석을 추가하려는 경우 다음과 같습니다.


function createPoint(x:number = 0, y:number = 0) {
  return [x, y];
}

9.3 Rest types 


ES6 나머지 연산자를 TypeScript 매개 변수 정의에 사용할 수도 있습니다. 해당 매개 변수의 유형은 배열이어야 합니다.


function joinNumbers(...nums: number[]): string {
    return nums.join('-');
}
joinNumbers(1, 2, 3); // '1-2-3'

10. Union types


JavaScript에서 변수는 때때로 여러 유형 중 하나를 갖습니다. 이러한 변수를 설명하기 위해 공용체 유형을 사용합니다. 예를 들어 다음 코드에서 x는 null 유형이거나 number 유형입니다.


let x = null;
x = 123;

x의 유형은 null | number로 설명 될 수 있습니다.


let x: null|number = null;
x = 123;

타입 표현식 s| t의 결과는 타입 s와 t의 집합 이론적 합집합입니다 (앞서 보았듯이 두 세트로 나타남).


stringify123() 함수를 다시 작성해 보겠습니다. 이번에는 매개 변수 콜백을 선택적으로 사용하고 싶지 않습니다. 항상 언급해야 합니다. 호출자가 함수를 제공하지 않으려면 명시 적으로 null을 전달해야 합니다. 다음과 같이 구현됩니다.


function stringify123(
  callback: null | ((num: number) => string)) {
  const num = 123;
  if (callback) { // (A)
    return callback(123); // (B)
  }
  return String(num);
}

다시 한 번, 우리는 라인 B에서 함수를 호출하기 전에 콜백이 실제로 함수인지 (라인 A) 확인해야 합니다. 확인하지 않으면 TypeScript는 오류를 보고 합니다.


10.1 Optional vs. undefined|T


유형 T의 선택적 매개 변수와 undefined | T 유형의 매개 변수는 매우 유사합니다. (선택적으로, 선택적 속성들도 마찬가지입니다.)


주요 차이점은 선택적 매개 변수를 생략 할 수 있다는 것입니다.

function f1(x?: number) { }
f1(); // OK
f1(undefined); // OK
f1(123); // OK

그러나 undefined | T 유형의 매개 변수는 생략 할 수 없습니다.


function f2(x: undefined | number) { }
f2(); // error
f2(undefined); // OK
f2(123); // OK

10.2 null 및 undefined 값은 일반적으로 유형에 포함되지 않습니다


많은 프로그래밍 언어에서 null은 모든 유형의 일부입니다. 예를 들어, 매개 변수 유형이 Java의 문자열 인 경우 null을 전달하면 Java가 불평하지 않습니다.


반대로 TypeScript에서 undefined와 null은 분리 된 분리 된 유형에 의해 처리됩니다. 허용하지 않으려면 undefined | string 및 null | string과 같은 유형 공용체가 필요합니다.


11. Typing objects


배열과 마찬가지로 객체는 JavaScript에서 두 가지 역할을 수행합니다 (때로는 혼합되거나 더 역동적임).

  • Records : 개발시 알려진 고정 된 양의 속성. 각 속성은 다른 유형을 가질 수 있습니다.
  • Dictionaries : 개발시 이름을 알 수 없는 임의의 양의 속성. 모든 속성 키 (문자열 및 / 또는 기호)는 속성 값과 동일한 유형을 갖습니다.

이 블로그 게시물에서는 사전 개체를 무시합니다. 따로,지도는 일반적으로 사전에 더 나은 선택입니다.


11.1 인터페이스를 통해 객체를 레코드로 입력


인터페이스는 레코드로 객체를 설명합니다. 예를 들면 다음과 같습니다.


interface Point {
  x: number;
  y: number;
}

TypeScript 유형 시스템의 큰 장점 중 하나는 명목상이 아니라 구조적으로 작동한다는 것입니다. 즉, Point 인터페이스는 적절한 구조를 가진 모든 객체와 일치합니다.


function pointToString(p: Point) {
  return `(${p.x}, ${p.y})`;
}
pointToString({x: 5, y: 7}); // '(5, 7)'

대조적으로, Java의 명목형 시스템은 인터페이스를 구현하기 위해 클래스가 필요합니다.


11.2 Optional properties


속성을 생략 할 수 있으면 이름 뒤에 물음표를 넣습니다.


interface Person {
  name: string;
  company?: string;
}

11.3 Methods


인터페이스에는 다음과 같은 메소드도 포함될 수 있습니다.


interface Point {
  x: number;
  y: number;
  distance(other: Point): number;
}

12. 타입 변수와 제네릭 타입


정적 입력을 사용하면 두 가지 수준이 있습니다.

  • 개체 수준에 값이 있습니다.
  • 유형은 메타 수준으로 존재합니다.

비슷하게:

  • 일반 변수는 객체 수준에 존재합니다.
  • 메타 수준에 존재하는 유형 변수도 있습니다. 값이 유형 인 변수입니다.

정규 변수는 const, let 등을 통해 도입됩니다. 유형 변수는 꺾쇠 괄호 (<>)를 통해 도입됩니다. 예를 들어 다음 코드에는 <T>를 통해 도입 된 형식 변수 T가 포함되어 있습니다.

interface Stack<T> {
  push(x: T): void;
  pop(): T;
}

Type 매개 변수 T가 Stack 본문 내부에 두 번 나타나는 것을 볼 수 있습니다. 따라서 이 인터페이스는 다음과 같이 직관적으로 이해할 수 있습니다.

  • Stack은 모두 주어진 유형 T를 갖는 값의 스택입니다. 스택을 언급 할 때마다 T를 채워야 합니다. 다음에 어떻게 하는지 살펴 보겠습니다.
  • .push() 메소드는 T 유형의 값을 허용합니다.
  • .pop() 메소드는 T 유형의 값을 리턴합니다.

스택을 사용하는 경우 유형을 T에 지정해야 합니다. 다음 코드는 더미 스택을 보여 주며, 목적은 인터페이스와 일치하는 것입니다.


const dummyStack: Stack<number> = {
  push(x: number) {},
  pop() { return 123 },
};


12.1 Example: Maps


Maps는 일반적으로 TypeScript에서 입력됩니다. 예를 들면 다음과 같습니다.


const myMap: Map<boolean,string> = new Map([
  [false, 'no'],
  [true, 'yes'],
]);

12.2 함수의 타입 변수


함수 (및 메소드)도 유형 변수를 도입 할 수 있습니다.


function id<T>(x: T): T {
  return x;
}

이 함수는 다음과 같이 사용합니다.


id<number>(123);

형식 유추로 인해 type 매개 변수를 생략 할 수도 있습니다.


id(123);

12.3 유형 매개 변수 전달


함수는 타입 매개 변수를 인터페이스, 클래스 등에 전달할 수 있습니다.


function fillArray<T>(len: number, elem: T) {
  return new Array<T>(len).fill(elem);
}

이 코드에서는 유형 변수 T가 세 번 나타납니다.

  • fillArray<T>: 타입 변수 소개
  • elem: T: 유형 변수를 사용하고 인수에서 선택하십시오.
  • Array<T>: T를 Array 생성자에 전달하십시오.

즉, Array <T>의 유형 T를 명시 적으로 지정할 필요가 없습니다. 이는 elem 매개 변수에서 유추됩니다.


const arr = fillArray(3, '*');
  // Inferred type: string[]

13. 결론


앞에서 본 코드를 이해하기 위해 배운 것을 사용합시다 :


interface Array<T> {
  concat(...items: Array<T[] | T>): T[];
  reduce<U>(
    callback: (state: U, element: T, index: number, array: T[]) => U,
    firstState?: U): U;
  ···
}

이것은 이 인터페이스를 사용할 때마다 채워야 하는 요소 유형이 T 인 Array에 대한 인터페이스입니다.

  • method.concat()는 0 개 이상의 매개 변수가 있습니다 (rest 연산자를 통해 정의 됨). 이러한 각 매개 변수의 유형은 T [] | T입니다. 즉, T 값의 배열이거나 단일 T 값입니다.
  • method.reduce()는 고유 한 유형 변수를 도입합니다. U는 다음 엔티티가 모두 동일한 유형 (지정하지 않아도 자동으로 추론 됨)을 갖는다는 사실을 나타냅니다.
    callback()의 ​​매개 변수 상태 (함수)
    callback() 결과 
    선택적 매개 변수 firstState of .reduce()
    .reduce()의 결과

콜백은 또한 유형이 배열 요소와 유형 T가 같은 매개 변수 요소, 숫자 인 매개 변수 색인 및 T 값을 가진 매개 변수 배열을 가져옵니다.