# ts 기본 문법
간단하게 TS내에서 원시값들이 어떻게 사용되는지 살펴보고,
vue.js에서는 어떻게 사용하는지 알아봅니다.
# 기본 타입
1. boolean
const bol: boolean = true;
2. number
const naturalNumber: number = 100;
const integer: number = 0.1;
3. string
const hangle: string = "한글";
4. null / undified
const a: null = null;
const b: undefined = undefined;
# 참조 타입
1. object
- 필수 속성 : 해당 속성이 없으면 에러 도출
const required: { name: string; age: number } = { name: "nkh", age: 999 };
- 선택 속성 : 꼭 없어도 되는 속성 하지만 있다면 타입을 맞춰야함,
?를 붙인다
const selection: { name: string; age?: number } = { name: "nkh" };
- 읽기 전용 속성 : 읽기만 가능하고 재할당 금지, cons와 비슷한 기능
const readOnly: {readOnly name: string} = {name: "nkh"}
//readOnly.name = 'error' - 재할당 불가
2. array
// string만 받는 배열
const onlyString: string[] = ["a", "b"];
// 제네릭
const onlyString: Array<string> = ["a", "b"];
// number만 받는 배열
const onlyNumber: number[] = [1, 2];
// 제네릭
const onlyNumber: Array<number> = [1, 2];
//여러가지 오면 any 타입으로 정의
3. function
//인자로 strin, number 받아 object 리턴할 때
function stringOrNumber(str: string, num: number): object {
return { str, num };
}
stringOrNumber("nkh", 999);
//인자로 객체 받을 때
function functionType(labelObj: { label: string }): object {
return labelObject;
}
functionalType({ label: "asd" });
# 특별한 타입
1. any
모든 타입이 사용 가능하다. 사실상 자바스크립트와 다를 바 없음
코드 작성 시점에서 이 값이 뭘 받을 지 모를 때, 알 수 없는 값으로 표기
>vue.js에서는ts-lint가any값을unknown으로 바꾸라 가이드 함
const anything: any = 123;
const anything: unknown = 123;
2. void
null 혹 undified 값만을 가지며, 다른 값을 가지면 오류를 도출 한다.
return 값이 없는 함수에 사용한다.
>vue.js내에서는 라이프사이클create함수에서 사용한다.
function noting(): void {}
3. never
절대로 발생하지 않는 값으로 에러 핸들링 함수에서 사용한다.
주로 함수의 리턴 타입으로 에러가 발생할 경우 에러를 무시하고 계속 진행시키는 역할을 합니다.
또는 대체 불가한 값을 만들 때 사용한다. 재할당 불가
function errorThrow(): never {
//에러 발생한 경우 중지하지 않고 throw 함수 실행
throw new Error("error");
}
# type
//많이 쓰일 타입을 사전에 정의
type UUID = number
test():UUID { return : 1}
# union 타입
- union 타입은 하나의 변수에 여러 타입을 지정할 수 있습니다. 여러 타입을 지정하고 싶은 경우
|를 사용합니다.
let value: string | number = "foo";
value = 100; //ok
value = "bar"; //ok
value = true; //error
# union 인터셉션
|는 또는 이라면&는 and 입니다.
interface Test {
name: string;
skill: string;
}
interface Test2 {
name: string;
age: string;
}
function ask(someone: Test | Test2) {
console.log(someone.name); // interface의 공통 속성으로 접근 가능
// someone.skill, age는 공통속성이 아니므로 접근 불가능
// 접근하고 싶다면 타입 가드로, 하나의 타입만 필터링 한 경우만 활용 가능
}
// &를 이용하면 3개의 속성 활용 가능 (인터섹션)
function ask(someone: Test & Test2) {
// Test와 Test2 두개의 interface를 포함하게 타입 정의
console.log(someone.name);
console.log(someone.skill);
console.log(someone.age);
}
- |를 쓰면 함수 호출시 두개의 인터페이스 중 1개만 보장해주면 되나, &를 쓰면 함수 호출시 두개의 인터페이스 타입을 다 보장해줘야하므로 |를 좀 더 많이 쓴다.
# union 타입 가드
- 여러 타입을 사용하면 해당 값의 타입에 따라 분기 처리할 때가 있습니다. 이럴 경우 각 타입에 따라 조건문을 만들어 주시면 됩니다.
function unionIter(value: string | number): number {
if (typeof value === "string") {
return value.length;
}
return value;
}
# interface
인터페이스는 여러가지 타입을 갖는 프로퍼티로 이루어진 새로운 타입을 정의하는 것
api를 통해 받아올 객체 또는 배열의 속성 타입 값을 미리 정의하여 사전 에러 차단
interface 값이 너무 많아지면@module로 interface 값만 따로 관리
변수 선언과 동일하게 선택 속성, 읽기전용(readOnly) 사용가능
interface에 없는 값 쓰고 싶을때as interface이름추가
interface로 union type으로 타입 정의 가능
<script lang="ts">
export interface Todo {
id: number;
name: string;
things?: object;
readonly password: number;
}
export default Vue.extend({
data() {
return {
//위에 정의한 Todo 인터페이스 타입을 져온다.
toDos: [] as Array<Todo>
};
},
methods: {
AddData(data: Todo): Array {
//Todo 인터페이스에 없는 'wantToAdd' 속성을 넣으려면 뒤에 'as Todo'를 넣는다.
this.toDos.push({
id: 1,
name: nkh,
password: 1234,
wantToAdd: "add"
} as Todo);
return data;
}
}
});
</script>
# type vs interface
- Use an interface instead of a type literal.tslint(interface-over-type-literal)
- type은 리터럴, interface는 object 형태에 사용하라 가이드 한다.
export type TSomeMemberTier = "Basic" | "Premium" | "Admin";
export interface SomeMember {
name: string;
age: number;
address: string;
tier: TSomeMemberTier;
}
# type의 특정 property만 제외
type XYZ = {
x: number;
y: number;
z: number;
};
// y, z 속성을 제외하여 아래처럼 만들고 싶다
type X = { x: number };
- ts 버전 3.5이상에서는
Omit을 사용하면 됩니다.
type X = Omit<XYZ, "x" | "y">;
// type X = { x: number };
# class에서 ts 사용 예제
class Person {
// 이 클래스안에서만 사용한다면 private
private name: string;
public age: number;
// 값 읽기만 가능, set 불가
readonly log: string;
constructor(name: string, age: numnber) {
this.name = name;
this.age = age;
}
}
# 제네릭
- 한가지 타입보다 여러 타입에서 동작하는 컴포넌트를 생성하는데 사용
- 함수의 파라미터로 받아 내부로직을 돌리는 것
// 1. 어떤 타입을 받을 건지 먼저 정의 (logText<T>)
// 2. params 타입으로 정의 (text: T)
function logText<T>(text: T): T {
console.log(text);
return text;
}
// 3. 함수를 호출할때 타입 정의
const str = logText<string>("a");
str.split(""); // string으로 정의했기때문에 split 가능
const login = logText<boolean>(true); // type: boolean
# 제네릭과 유니온의 공통점
제네릭과 유니온 타입이 둘다 여러 타입을 동시에 다룬다는 점에서 공통점이 있다
# 유니온의 단점
유니온 타입의 경우 두 타입의 공통된 메소드만 타입 추적을 해준다는 단점이 있고, 받은 값을 그대로 리턴시, 리턴 받은 값고 하나의 타입이 아닌 유니온 타입으로 지점되는 문제가 있다
function logText(text: string | number) {
// string과 number의 공통된 메소드만 사용 가능
return text;
}
// a의 타입은 string | number 이다. 그렇기 때문에 split 이용 불가
const a = logText("a");
// error: split does not exist on type string | number
a.split("");
위 처럼 유니온은 타입 가드를 한다 해도 return되는 값이 명확하지 않으므로 제네릭을 쓰는 것이 더 좋다
# 제네릭 타입 제한
- 제네릭으로 들어온 타입에 임의로 지정한 inteface를 확장한다
interface LengthType {
length: number;
}
// 제네릭으로 받은 타입 T는 lengthType의 하위 타입이다. 즉, length: number는 무조건 포함됨
function logTextLength2<T extends LengthType>(text: T): T {
text.length;
return text;
}
logTextLength2("dd");
logTextLength2({ length: 3, q: 22 });
- 제네릭으로 들어온 타입에 임의로 지정한 interface만 사용하도록 제한
interface ShoppingItem {
name: string;
price: number;
stock: number;
}
// ShoppingItem에 있는 키중 한가지가 T가 된다 -> 함수는 'name' | 'price' | 'stock'만 쓸 수 있다.
function getShoppingItemOption<T extends keyof ShoppingItem>(item: T): T {
return item;
}
getShoppingItemOption("name");
# interface에 제네릭을 넣는 법
interface Dropdown<T, G> {
value: T;
selected: G;
}
const obj2: Dropdown<string, boolean> = { value: "abc", selected: false };
# interface 종합 예제
interface DropDownItem<T> {
value: T;
selected: boolean;
}
const emails: DropDownItem<string>[] = [
{ value: "naver.com", selected: true },
{ value: "gmail.com", selected: false },
{ value: "hanmail.com", selected: false }
];
const numberOfProducts: DropDownItem<number>[] = [
{ value: 1, selected: true },
{ value: 2, selected: false },
{ value: 3, selected: false }
];
// email과 number 둘다 받아야하는 상황
function createDropdownItem<T extends { toString: Function }>(
item: DropDownItem<T>
): HTMLOptionElement {
const option = document.createElement("option");
option.value = item.value.toString();
option.innerText = item.value.toString();
option.selected = item.selected;
return option;
}
emails.forEach(function(email) {
const item = createDropdownItem<string>(email);
const selectTag = document.querySelector("#email-dropdown");
selectTag?.appendChild(item);
});
numberOfProducts.forEach(function(products) {
const item = createDropdownItem<number>(products);
});
# async / await
- 설명보다 예시 코드가 이해하기 편할 것 입니다.
interface Employee {
id: number;
employee_name: string;
employee_salary: number;
employee_age: number;
profile_image: string;
}
const fetchEmployees = async (): Promise<Array<Employee> | string> => {
const api = "http://dummy.restapiexample.com/api/v1/employees";
try {
const response = await fetch(api);
const { data } = await response.json();
return data;
} catch (error) {
if (error) {
return error.message;
}
}
};
const fetchEmployee = async (
url: string,
id: number
): Promise<Record<string, string>> => {
const response = await fetch(`${url}/${id}`);
const { data } = await response.json();
return data;
};
# vue.js 종합 예시
<template>
<div class="hello">
<p>{{ changeString }}</p>
<p>{{ fullName }}</p>
<p>{{ selection }}</p>
<p>{{ readOnly }}</p>
<p>{{ toDos }}</p>
<p>unnion : {{ test2 }}</p>
<p>promise : {{ fetchData }}</p>
<button @click="noting">change changeString</button>
<button @click="promiseTest">axios</button>
</div>
</template>
<script lang="ts">
import Vue from "vue";
import axios from "axios";
//변수 인터페이스
export interface Todo {
id: number | true;
name: string;
}
interface SquareConfig {
color?: string;
width?: number;
readonly test: string;
}
type UUID = string;
export default Vue.extend({
data() {
return {
changeString: "test" as string,
testArray: [123, 234] as unknown[],
neverType: "never" as never,
firstName: "noh" as string,
lastName: "kh" as string,
toDos: [] as Array<Todo>,
union: "asdasd" as string | number,
fetchData: [] as Array<unknown>
};
},
computed: {
fullName(): string {
return this.firstName + this.lastName;
},
selection(): object {
const select: { name: string; age?: number } = { name: "nkh" };
return select;
},
readOnly(): object {
const read: { readonly name: string } = { name: "nkh" };
return read;
},
test(): UUID {
return "test";
},
test2(): number {
if (typeof this.union === "string") {
return this.union.length;
}
return this.union;
}
},
methods: {
//unnion 분기처리
te(union: string | number): string {
if (typeof union === "number") {
return String(union);
}
return union;
},
createSquare(config: SquareConfig): void {
console.log(config);
},
functionalType(labelObj: { label: string }): object {
//생성자에 없는 값 쓰고 싶을때
this.createSquare({
colour: "red",
width: 100,
test: "changeString"
} as SquareConfig);
return labelObj;
},
noting(): void {
this.changeString = "asd";
this.union = 123;
const objectDec: { name: string | boolean; age: number } = {
name: "name",
age: 123
};
console.log(objectDec);
//변수형 인터페이스
this.toDos.push({ id: 123, name: "asd" });
this.functionalType({ label: "asd" });
},
async promiseTest(): Promise<unknown> {
const api = "http://dummy.restapiexample.com/api/v1/employees";
const res = await axios.get(api);
this.fetchData = res.data.data;
return res.data.data;
}
}
});
</script>
# 날짜 비교
날짜를 비교할때 new Date객체를 이용하여 빼기 연산을 하는데, 이때 date 타입이 number가 아니기 때문에 타입스크립트에서는 에러를 낸다.
그래서 +를 이용하여 날짜객체를 숫자로 바꾸면 연산이 된다.
(res: bookingType[]) => [...res].sort((a, b) => +new Date(a.start_on) - +new Date(b.start_on)),