[TypeScript] chap.1

 

 

 

설치 및 세팅

1. nodejs 설치

2. 타입스크립트 설치

npm install -g typescript
tsc -w // 자바스크립트로 저장시 자동 컴파일

 

 

* React에서 Typescript 사용

- 이미 있는 프로젝트

npm install --save typescript @types/node @types/react @types/react-dom @types/jest

 

- 새로운 프로젝트

npx create-react-app my-app --template typescript

 

 

* Vue에서 Typescript 사용

vue add typescript
<script lang="ts">
  
</script>

lang 옵션을 키고 사용할 수 있다.

 

 

* tsconfig.json

- 타입스크립트 ts 파일들을 .js 파일로 변환할 때 어떻게 변환할 것인지 세부설정하는 파일

{
 "compilerOptions": {

  "target": "es5", // 'es3', 'es5', 'es2015', 'es2016', 'es2017','es2018', 'esnext' 가능
  "module": "commonjs", //무슨 import 문법 쓸건지 'commonjs', 'amd', 'es2015', 'esnext'
  "allowJs": true, // js 파일들 ts에서 import해서 쓸 수 있는지 
  "checkJs": true, // 일반 js 파일에서도 에러체크 여부 
  "jsx": "preserve", // tsx 파일을 jsx로 어떻게 컴파일할 것인지 'preserve', 'react-native', 'react'
  "declaration": true, //컴파일시 .d.ts 파일도 자동으로 함께생성 (현재쓰는 모든 타입이 정의된 파일)
  "outFile": "./", //모든 ts파일을 js파일 하나로 컴파일해줌 (module이 none, amd, system일 때만 가능)
  "outDir": "./", //js파일 아웃풋 경로바꾸기
  "rootDir": "./", //루트경로 바꾸기 (js 파일 아웃풋 경로에 영향줌)
  "removeComments": true, //컴파일시 주석제거 

  "strict": true, //strict 관련, noimplicit 어쩌구 관련 모드 전부 켜기
  "noImplicitAny": true, //any타입 금지 여부
  "strictNullChecks": true, //null, undefined 타입에 이상한 짓 할시 에러내기 
  "strictFunctionTypes": true, //함수파라미터 타입체크 강하게 
  "strictPropertyInitialization": true, //class constructor 작성시 타입체크 강하게
  "noImplicitThis": true, //this 키워드가 any 타입일 경우 에러내기
  "alwaysStrict": true, //자바스크립트 "use strict" 모드 켜기

  "noUnusedLocals": true, //쓰지않는 지역변수 있으면 에러내기
  "noUnusedParameters": true, //쓰지않는 파라미터 있으면 에러내기
  "noImplicitReturns": true, //함수에서 return 빼먹으면 에러내기 
  "noFallthroughCasesInSwitch": true, //switch문 이상하면 에러내기 
 }
}

 

 

 

기본 타입
let 이름 :string = 'kim'; // string
let 나이 :number = 20; // number
let 결혼했니 :boolean = false; // boolean

let 회원들 :string[] = ['kim', 'park'] // array
let 내정보 : { age : number } = { age : 20 } // object

// **기본적으로 변수 생성 시 타입이 알아서 지정되기 때문에 일일이 할 필요는 없다.

 

 

 

 

union 타입
let 이름: string | number = 'kim';
let 나이: (string | number) = 100; // 같음

///////////////////////////////////////////

var 어레이: number[] = [1,'2',3]
var 오브젝트: {data : number} = { data : '123' }

var 어레이: (number | string)[] = [1,'2',3]
var 오브젝트: {data : (number | string) } = { data : '123' }

 

 

 

any, unknown 타입

* any

아무 자료나 대입할 수 있는 타입. (타입스크립트의 기능을 해제하는 타입)

let 이름: any = 'kim';
이름 = 123;
이름 = undefined;
이름 = [];

 

 

* unknown

아무 자료나 대입할 수 있는 타입. (타입은 uknown으로 지정되어 다른 타입과는 에러발생. any는 아님)

let 이름: unknown;

let 변수1: string = 이름;
let 변수2: boolean = 이름;
let 변수3: number = 이름;

///////////////////////////

let 이름: unknown;
이름[0];
이름 - 1;
이름.data;

 

타입스크립트는 언어가 엄격하기 때문에 변경하려는 변수의 타입이 확실해야만 연산을 수행해준다. -1, +1 같은 경우는 number 타입일 때만, .name 등은 object 타입 일때만 사용이 가능하다.

 

 

 

함수 & void

1. 함수로 들어오는 자료 (파라미터)

2. 함수에서 나가는 자료 (return)

function 내함수(x :number) :number { 
  return x * 2 
}

 

 

- void

function 내함수(x :number) :void { 
  return x * 2 //여기서 에러남 
}

함수에서 return을 방지할 때 사용한다.

 

 

- 파라미터가 옵션인 경우

function 내함수(x? :number) { // == x : number | undefined

}
내함수(); //가능
내함수(2); //가능

x? :number은 x :number | undefined 와 같은 의미이다.

 

 

 

 

 

Narrowing & Assertion

타입을 확정할 때 쓰는 문법.

 

* Type Narrowing

function 내함수(x :number | string){
  if (typeof x === 'number') {
    return x + 1
  } 
  else if (typeof x === 'string') {
    return x + 1
  }
  else { // 함수안에서 if쓸 때 else가 없으면 에러날 수도 있으니 쓰자
    return 0
  }
}

 

 

* Type Assertion

function 내함수(x :number | string){ 
    return (x as number) + 1 
}
console.log( 내함수(123) )

해당하는 변수를 number로 지정한다. (내가 꼭 ! number 타입이 들어올 것이라는 확신이 있을 때 사용한다.)

 

특징:

1. union 타입 중에서 하나의 정확한 타입으로 지정할 때 사용 (형 변환처럼 사용하면 에러 남)

2. 타입실드를 임시로 해제한다고 생각하면 된다. (타입을 바꿔주는건 아니다)

 

Assertion은

1. 타입에러가왜 나는지 모르겠는 상황에서 에러 해결용으로 사용

2. 어떤 타입이 들어갈지 확실히 아는데 컴파일 상에서 에러가 날 때 사용.

 

**웬만하면 Narrowing 쓰는게 더 나음.

 

 

 

type & readonly

* type

타입의 정의가 너무 길거나 복잡하게 나열된 타입을 변수에 담아 사용할 수 있다.

type Animal = string | number | undefined;
let 동물 :Animal;
type 사람 = {
  name : string,
  age : number,
}

let teacher :사람 = { name : 'john', age : 20 }

 

 

- object의 속성이 선택사항일 때

type Square = {
  color? : string,
  width : number,
}

let 네모2 :Square = { 
  width : 100 
}

함수의 파라미터와 마찬가지로 object에서도 ?를 통해 string | undefined 형태로 선택적으로 사용할 수 있다.

 

 

- type 키워드 여러개 합치기

type Name = string;
type Age = number;
type NewOne = Name | Age;

or 연산자를 이용해서 union 타입으로 만들 수 있다.

 

type PositionX = { x: number };
type PositionY = { y: number };
type XandY = PositionX & PositionY
let 좌표 :XandY = { x : 1, y : 2 }

and(&) 기호를 쓰면 object 안의 두개의 속성을 합칠 수 있다. (extend)

 

type Name = string;
type Name = number; // 불가

type 키워드는 재정의가 불가능하다.

 

 

* readonly

object 속성을 const처럼 재 할당할 시에 값이 변경하는걸 미리 감지하고 차단한다

type testType = {
  readonly name : string,
}

let 이름 :testType = {
  name : '홍길동'
}

이름.name = '고길동' //readonly라서 에러남

컴파일 시에 에러만 출력되는것이고, 자바스크립트로 변환하면 문제없이 작동한다.

 

 

 

Literal type

특정 글자나 숫자만 가질 수 있게 그것으로 타입 제한을 두는 타입

let john :'대머리';
let kim :'솔로';

let 방향: 'left' | 'right';
방향 = 'left';

function 함수(a : 'hello') : 1 | 0 | -1 {
  return 1 
}

 

- as const

1. 타입을 object의 value로 바꿔준다. (타입을 'kim'으로 바꿔준다)

2. object 안에 있는 모든 속성을 readonly로 바꾼다. (변경하면 에러)

var 자료 = {
  name : 'kim'
}
function 내함수(a : 'kim') { // 'kim' 타입

}
내함수(자료.name) // string 타입 => 에러

function의 파라미터 변수 a는 'kim'이라는 리터럴 타입이고, 자료.name으로 가져오는 'kim'은 string 타입이다.

 

var 자료 = {
  name : 'kim'
} as const;

function 내함수(a : 'kim') {

}
내함수(자료.name)

해결책:

1. object 만들 때 object의 타입을 지정한다.

2. assertion을 쓴다 (ex. as 'kim')

3. as const를 object 자료형에 붙인다.

 

 

 

function & method의 type alias

* function

type NumOut = (x : number, y : number ) => number ;

let ABC :NumOut = function(x,y){
  return x + y
} // 함수 표현식

함수에 type 지정하기 (함수 선언문 형태로는 type을 만들 수 없다. 사용할 때도 표현식으로 사용)

 

 

* method

type 회원정보타입 = {
    name: string,
    plusOne: (a: number) => number,
    changeName: () => void
};

let 회원정보 :회원정보타입 = {
    name: 'kim',
    plusOne(a: number): number{
        return a + 1;
    },
    changeName: () => {
        console.log('안녕');
    }
};
회원정보.plusOne(1);
회원정보.changeName();

 

 

 

HTML

- 세팅

{
    "compilerOptions": {
        "target": "ES5",
        "module": "commonjs",
        "strictNullChecks": true // true로 설정 (false는 타입스크립트 쓰는 의미가..)
    }
}

null 체크 옵션인데 html 조작할 때 셀렉터로 찾으면 null이 발생하는 일이 많다.

 

 

- HTML 변경

  • narrowing
  • instanceof
  • assertion
  • optional chaining
  • strict false
let 제목 = document.querySelector('#title');
if(제목 != null){
    제목.innerHTML = '반가우요';
}
if(제목 instanceof Element){
    제목.innerHTML = '반가우요';
}
제목.innerHTML = '반가우요'; //as Element => assertion
if(제목?.innerHTML != undefined){
    제목.innerHTML = '반가우요';
}

 

 

- instanceof & optional chaining

let 링크 = document.querySelector('.link');

// instanceof
if(링크 instanceof HTMLAnchorElement){
    링크.href = 'https://kakao.com'
}

// optional chaining
let 버튼 = document.querySelector('#button');
버튼?.addEventListener('click', function(){
    
})

ex:

<a> =  HTMLAnchorElement

<img> = HTMLImageElement

<h4> = HTMLHeadingElement

 

 

 

 

 

class

- 필드값 타입지정

class Person {
  data = 0;
}

let john = new Person();
let kim = new Person();

console.log(john.data);
console.log(kim.data);

 

 

- constructor 타입지정

class Person {
  name;
  age;
  constructor ( a :string ){
    this.name = a;
    this.age = 20;
  }
}

constructor에도 default parameter나 rest parameter를 사용 할 수 있다.

 

 

- method 타입지정

class Person {
  
  add(숫자){ // Person의 prototype에 저장 됌
    console.log(숫자 + 1)
  }
}

add라는 함수를 prototype으로 사용할 수 있다.

 

 

 

interface

object 자료형의 타입을 편리하게 저장할 수 있다.

interface Square { 
  color :string, 
  width :number, 
} 

let 네모 :Square = { color : 'red', width : 100 }

 

 

- extends

interface Student {
  name :string,
}
interface Teacher {
  name :string,
  age :number,
}

///////////////////////////////////////

interface Student {
  name :string,
}
interface Teacher extends Student {
  age :number
}

let 학생: Student = {name: 'kim'}
let 선생: Teacher = {name: 'kim', age: 20 }

interface의 extends를 사용하면 extends 되는 object 안에 있는 것들을 복사해서 넣어준다.

 

 

* interface와 type의 차이점

- interface

interface Animal { 
  name :string 
} 
interface Cat extends Animal { 
  legs :number 
}

extends하기

 

interface Student {
  name :string,
}
interface Teacher {
  age :number
}

let 변수 :Student & Teacher = { name : 'kim', age : 90 }

intersection(&) 기호를 통한 extends와 유사하게 사용하기

 

// 에러 안남 (타입 명 중복 허용)
interface Animal { 
  name :string 
} 
interface Animal { 
  legs :number 
}

////////////////////////////

// 에러 남
interface Animal { 
  name :string 
} 
interface Dog extends Animal { 
  name :number 
}

타입이름 중복 선언시 extends한 것이랑 동일하게 동작하며 interface 안에 개별적 속성을 한꺼번에 가질 수 있다. type 선언을 자주하는 외부 라이브러리 이용시 type 선언을 덮어쓰기(override) 하기 편하다.

 

 

- type

type Animal = { 
  name :string 
} 
type Cat = Animal & { legs: number }

extends 대신에 intersection(&) 기호를 통해서 object 두개를 합칠 수 있다. (하지만 interface도 이렇게 사용할 수 있다.)

 

// 에러 남
type Animal = { 
  name :string 
} 
type Animal = { 
  legs :number 
}

///////////////////

type Animal = {name: string}
type Cat = {name: number} & Animal

let 야옹이 :Cat = {name: 'kim'}

타입이름 중복 선언시 에러난다. 타입을 intersaction(&) 했을 때, 타입 안에 개별적 속성이 겹치는 경우, 사용할 때 에러가 난다.

 

 

결론: object형태의 자료형은 interface로 만들고 다른형태(string, number, boolean ... 등 primitive 타입)의 자료형은 type 키워드로 만들어 사용하자.

 

 

 

'TypeScript' 카테고리의 다른 글

[TypeScript] chap.2  (0) 2021.12.17

+ Recent posts