function 함수(...a: number[]) {
// 여러 파라미터가 배열로 들어옴
console.log(a);
}
함수(1, 2, 3); //[ 1, 2, 3 ]
let arr = [1, 2, 3];
let arr2 = [4, 5];
let arr3 = [...arr, ...arr2];
console.log(...arr3); //1 2 3 4 5
let obj = { a: 'b', b: 'c' };
console.log({ ...obj }); //{ a: 'b', b: 'c' }
MDN spread 문법
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Spread_syntax
let [변수1, 변수2] = ['안녕', 100];
console.log(변수1, 변수2); //안녕 100
// let { student, age } = { student: true, age: 20 };
//사실 정확히는
let { student: student, age: age } = { student: true, age: 20 };
//student변수에는 오른쪽 객체의 student프로퍼티의 값을 할당해주세요~ 이런 의미
console.log(student, age); //true 20
// 응용
interface Info {
student: boolean;
age: number;
}
function 함슈({ student, age }: Info) {
console.log(student, age); //true 20
}
함슈({ student: true, age: 20 });
type FindMax = (...x: number[]) => number;
const findMax: FindMax = (...nums) => {
let maxNum: number = 0;
nums.forEach((num) => {
if (maxNum <= num) maxNum = num;
});
return maxNum;
};
findMax(5, 2, 3, 4);
const 연습2 = ({
user,
comment,
admin,
}: {
user: string;
comment: number[];
admin: boolean;
}) => {
console.log(user, comment, admin);
};
연습2({ user: 'kim', comment: [3, 5, 4], admin: false });
const 연습3 = ([a, b, c]: (number | string | boolean)[]) => {
console.log(a, b, c);
};
연습3([40, 'wine', false]);
function 함수(a: string | undefined) {
// a 가 undefined가 아니고 스트링임을 한 줄로.
if (a && typeof a === 'string') {
}
}
type Fish = { swim: string };
type Bird = { fly: string };
function 함슈(animal: Fish | Bird) {
// animal.swim -> 아직 확실하지 않아서 에러.
if ('swim' in animal) {
console.log(animal.swim);
} else if ('fly' in animal) {
console.log(animal.fly);
}
}
참고 인스턴스란?
생성자 함수 또는 클래스가 만들어낸 객체를 의미.
let 날짜 = new Date();
if (날짜 instanceof Date) {
console.log('날짜는 Date의 자식이에요!');
} //날짜는 Date의 자식이에요!
type Car = {
wheel: '4개';
color: string;
};
type Bike = {
wheel: '2개';
color: string;
};
function 예제3(x: Car | Bike) {
if (x.wheel === '4개') {
console.log('차에요');
} else if (x.wheel === '2개') {
console.log('바이크에요');
}
}
never
1. 절대 return 하지 않는다.
2. 함수 실행이 끝나지 않는다(endpoint 존재 X)함수에 never지정해주면 타입 에러가 뜸.
모든 함수에는 return을 직접 지정해주지 않으면
기본적으로 return undefined를 하고 있기 때문.
function 함수(): never {
throw new Error();
// 다음 코드를 실행하기 전에 Error로 인해 멈춤.
}
function 함수2(): never {
while (true) {
//코드
}
// 영원히 끝나지 않는 while 문.
}
function 함수3(param: string) {
if (typeof param === 'string') {
console.log(param);
} else {
console.log(param); // never
}
// param은 스트링만 들어올 수 있으니까 never
}
let 함수4 = function () {
throw new Error();
};
결론
- 굳이 사용되는 경우는 없고 never가 뜨면 왜 그런지 이해만
class Case1 {
name = 'kim';
}
class Case2 {
name;
constructor() {
this.name = 'kim';
}
}
// Case1, Case2 는 똑같다.
// 하지만 constructor의 파라미터를 이용해서
// 생성되는 인스턴스들의 속성 변경 가능.
class User {
public name = 'kim';
constructor(a: string) {
this.name = a;
}
public 프로토타입에추가되는함수() {
console.log('프로토타입함수');
}
}
let 유저1 = new User('park');
유저1.name = 'kaka';
console.log(유저1); // kaka
- 특정 속성을 보호하고 싶을 때
- 생성자 class에서 직접 조회는 가능함.
class User2 {
private name = 'kim';
constructor(a: string) {
this.name = a;
}
}
let 유저2 = new User2('park');
console.log(유저2.name);
// (에러문구) property name은 private이라
// User2 클래스 내에서만 수정, 사용이 가능하다.
사용 예시
class Example { name: string; private familyName: string = 'kim'; constructor(a: string) { this.name = a + this.familyName; } } let 예시 = new Example('호호'); 예시.name; // 호호KIM // familyName은 다른 사람이 변경하면 안될 일! // 중요 프로퍼티 등은 private를 이용해서 // 수정 등을 방지
class Example2 {
name: string;
private familyName: string = 'kim';
constructor(a: string) {
this.name = a + this.familyName;
}
성씨변경함수(a: string) {
//프로토타입
this.familyName = a;
}
}
let 시민 = new Example2('봄봄');
시민.성씨변경함수('park');
console.log(시민);
//Example2 { familyName: 'park', name: '봄봄kim' }
class Person {
name;
constructor() {
this.name = 'KAK';
}
}
class Person2 {
constructor(public name: string) {
// 이 자리의 파라미터는 자식의 name 속성에 할당해주세요
// => feild에 name과 컨스트럭터에 this.name = name 생략 가능.
}
}
let person1 = new Person2('이름');
console.log(person1);
//Person2 { name: '이름' }
class User {
x = 10;
}
class Copy extends User {}
const CopyUser = new Copy();
console.log(CopyUser); //Copy { x: 10 }
// private은 클래스나 인스턴스나 사용 불가능.
class Private {
private x = 10;
}
// extends 클래스에서 x 사용 불가.
class NewPrivate extends Private {}
class Protect {
protected x = 10;
}
// extends 클래스 내에서 x 사용 가능.
class NewProtec extends Protect {
doThis() {
this.x = 20;
}
}
class Uer {
static x = 10;
y = 20;
}
let 자식 = new Uer();
console.log(자식); // Uer { y: 20 }
x 는 부모만
console.log(Uer.x); //10
y는 부모가 못쓰고 자식만.
console.log(Uer.y); //undefined
private / protected / public 과 동시 사용 가능
class Uer2 {
private static x = 10;
y = 20;
}
class Apply {
static skill = 'JS';
intro = `${Apply.skill} 전문가입니다.`;
// this.skill 에러
// static은 부모만의 프로퍼티
}
const 철수 = new Apply();
console.log(철수);
// Apply { intro: 'JS 전문가입니다.' }
Apply.skill = 'TS';
const 영희 = new Apply();
console.log(영희);
// Apply { intro: 'TS 전문가입니다.' }
class 연습 {
private static x = 10;
public static y = 20;
protected z = 30;
}
/*
1. 필드값은 원래는 모든 User의 자식들에게 물려주는 속성이지만
x와 y에는 static 키워드가 붙었기 때문에 User.x 이런 식으로만 접근해서 쓸 수 있습니다.
User의 자식들은 x와 y를 쓸 수 없습니다.
2. private static x는 class 내부에서만 수정가능합니다.
3. public static y는 class 내부 외부 상관없이 수정가능합니다. public 키워드 지워도 똑같이 동작할 듯
4. protected z는 private 키워드와 유사하게 class 내부에서만 사용이 가능한데
약간 범위가 넓어서 extends로 복사한 class 내부에서도 사용할 수 있습니다.
*/
class 연습확장 extends 연습 {}
const practice = new 연습();
console.log(연습);
console.log(practice);
class 연습2 {
private static x = 10;
public static y = 20;
addOne(num: number) {
연습2.x += num;
}
printX() {
console.log(연습2.x);
}
}
class Square {
x;
y;
color;
constructor(a: number, b: number, c: string) {
this.x = a;
this.y = b;
this.color = c;
}
draw() {}
}
let 네모 = new Square(30, 30, 'red');
// 답
class Square {
constructor(
public width: number,
public height: number,
public color: string
) {}
draw() {
let a = Math.random();
let square = `<div style="position:relative;
top:${a * 400}px;
left:${a * 400}px;
width:${this.width}px;
height : ${this.height}px;
background:${this.color}"></div>`;
document.body.insertAdjacentHTML('beforeend', square);
}
}
let 네모 = new Square(30, 30, 'red');
네모.draw();
네모.draw();
네모.draw();
네모.draw();
//export(A.ts)
export let 변수 = 'kim';
export let 변수2 = 20;
export type Name = string;
export interface 인터페이스 {}
namespace 네임스페이스 {
export type Name = string | number;
}
module 네임스페이스와같음 {
}
// import(index.ts)
import { 변수, 변수2, Name} from './a';
console.log(변수, 변수2); //kim 20
let 이름: Name = 'park';
// import, export가 있기 전엔
// <script src='a.js />
// 이런식으로 불러왔음.
// 해당 소스 파일의 모든 코드를 불러오기 때문에
// 변수가 겹치는 등의 문제가 발생함.
// 그래서 그 당시엔 namespace 문법을 사용함.
///<referenc path='./a.ts' />
네임스페이스.Name;
function 함수(x: unknown[]) {
return x[0];
}
let a = 함수([4, 2]);
// a의 타입 unknown
// return이 숫자라도 자동 타입 변환하지 않음.
console.log(a + 1);
// error ->narrowing 해결 but 불편함.
function 제네릭<아무거나>(x: 아무거나[]): 아무거나 {
return x[0];
}
//파라미터로 타입을 지정 가능.
let b = 제네릭<number>([4, 2]);
console.log(b + 1); // 해결
let c = 제네릭<string>(['4', '2']);
let d = 제네릭([true, false]);
// typeof d === boolean (지정안해줘도 알아서 해줌.)
function 함슈<MyType>(x: MyType) {
return x - 1;
// 미리 x는 any, bigInt, number 등의 타입만 가능하다고
// 에러를 띄워줌.
}
// narrowing 번거로우니
function 함슈<MyType extends number | bigint>(x: MyType) {
return x - 1;
}
let result = 함슈<number>(100);
interface LengthCheck {
length: number;
}
function lengthChecker<MyType extends LengthCheck>(x: MyType) {
return x.length;
}
let fw = lengthChecker<string>('hi');
let f = lengthChecker<number>(1234);
class 제너릭<MyType> {
a: MyType;
constructor(x: MyType) {
this.a = x;
}
}
const 뉴클 = new 제너릭<number>(2);
console.log(뉴클);
//제너릭 { a: 2 }
//문자를 집어넣으면 문자의 갯수, array를 집어넣으면 array안의 자료 갯수를 콘솔창에 출력해주는 함수
function 함수<T extends string | string[]>(x: T) {
console.log(x.length);
}
interface Animal {
name: string;
age: number;
}
let data = '{"name" : "dog", "age" : 1 }';
function convert<T extends Animal>(x: string): T {
return JSON.parse(x);
}
// 또는 extends 안쓰고
convert<Animal>(data);
class Person<T> {
name;
constructor(a: T) {
this.name = a;
}
}
let a = new Person<string>('어쩌구');
a.name; //any 타입이 아닌 제너릭에 따라 타입지정되도록.
let dog: (string | boolean)[] = ['dog', true];
// 첫 인덱스 요소는 스트링 두번째는 불리언!
let dog1: [string, boolean] = ['dog', true];
//옵셔널도 가능.
let cat: [string, boolean?] = ['dog']; //에러 X
//주의
let cat1: [string, boolean?, number];
// 요소 순서가 불확실해져서 옵셔널은 마지막 요소만
// number[] 대신 tuple도 가능.
function 함수(...x: [number, number, number, number]) {
console.log(x);
}
함수(1, 2, 3, 4);
let arr = [1, 2, 3];
let arr2 = [4, 5];
let combine = [...arr, ...arr2];
//이렇게 쓰면 됨.
let tuple: [number, number, ...number[]] = [4, 5, ...arr];
.js 파일의 변수를 .ts에서 이용하기
// .js
var a = 10;
var b = { name: 'kim' };
// .ts
declare let a: number;
console.log(a + 1);
let name // error
뜨는 이유
ts기본 파일에 이미 let name이 있기 때문.
ambient module 예시
//data.ts
var a = 10;
// index.ts
console.log(a); // error X
let a; // cannot redeclare error
//data.ts
var a = 10;
export {}
// index.ts
let a; //good
// data.ts
let a = 10;
declare global {
type Dog = string;
}
export { a };
// index.ts
import { a } from './data.js';
console.log(a + 1);
let b: Dog = 'kim';
// ## 파일명.d.ts
// 타입정의 보관용 파일
export type Age = number;
export interface Person {
name: string;
}
// ## index.ts
import { Age } from './test';
// 또는
import * as 변수명 from './test';
let age: Age;
// tsconfig.json
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"noImplicitAny": true,
"strictNullChecks": true,
"declaration": true
}
}
이렇게 저장된다.
//(index.ts)
let 이름 :string = 'kim';
let 나이 = 20;
interface Person { name : string }
let 사람 :Person = { name : 'park' }
--
//(index.d.ts)
declare let 이름: string;
declare let 나이: number;
interface Person {
name: string;
}
declare let 사람: Person;
주의!
- d.ts파일은 글로벌 모듈이 아니다.
- 글로벌 모듈로 사용하고 싶다면
{ "compilerOptions": { "target": "es5", "module": "commonjs", "noImplicitAny": true, "strictNullChecks": true, "declaration": true, "typeRoots": ["./types"] } }
- typeRoots 폴더 내의 d.ts파일 타입들을 전역으로 사용가능하게 하는 옵션.
// types폴더의 common폴더에 위치한 test.d.ts 파일.
type Age = number;
// index.ts
let 나이: Age = 30;
예)
npm i -save @types/jquery
node modules 파일의 @types에 추가됨.
주의할 점은 typeRoots 옵션을 설정해두면 자동으로 적용이 안되기 때문에
해당 설정을 끄는 것을 권장.
iterface는 객체의 타입을 지정할 때 사용한다.
또한 class의 타입을 확인할 때도 사용하는데 이 때 implements가 필요!
interface CarType {
model: string;
price: number;
}
class Car implements CarType {
model: string;
constructor(a: string) {
this.model = a;
}
}
error: Class 'Car' incorrectly implements interface 'CarType'.
Property 'price' is missing in type 'Car' but required in type 'CarType'.
!!
implements는 타입을 지정해주지 않고 속성 확인만을 해준다!
interface CarType {
model : string,
tax : (price :number) => number;
}
class Car implements CarType {
model; ///any 타입됨
tax (a){ ///a 파라미터는 any 타입됨
return a * 0.1
}
}
// 기존
interface String {
name: string;
age: string;
location: string;
}
//모든 속성을 한번에 타입지정하고 싶을 때
interface String2 {
[key: string]: string | number;
}
interface StringOnly {
age : number, ///에러
[key: string]: string,
}
interface StringOnly {
age : string, ///가능
[key: string]: string,
}
interface StringOnly {
age : number, ///가능
[key: string]: string | number,
}
interface StringOnly {
[key: number]: string,
}
let obj :StringOnly = {
0 : 'kim'
1 : '20',
2 : 'seoul'
}
// string으로 지정해도 에러 안남.
interface StringOnly {
[key: string]: string;
}
// 기존
interface MyType {
'font-size': {
'font-size': {
'font-size': number;
};
};
}
// Recursive Index Signatures
interface MyType {
'font-size': MyType | number;
}
let css: MyType = {
'font-size': {
'font-size': {
'font-size': 14,
},
},
};
interface MyObj {
[key: string]: string | number;
}
let obj: MyObj = {
model: 'k5',
brand: 'kia',
price: 6000,
year: 2030,
date: '6월',
percent: '5%',
dealer: '김차장',
};
interface MyObj {
'font-size': number;
[key: string]: MyObj | number;
}
let obj: MyObj = {
'font-size': 10,
secondary: {
'font-size': 12,
third: {
'font-size': 14,
},
},
};
let obj = {
name: 'kim',
};
console.log(Object.keys(obj));
// 키를 배열로 반환.
//[ 'name' ]
object의 키를 이용해서 union type을 만들어줌
interface Person {
age: number;
name: string;
}
type PersonKeys = keyof Person
// 'age' 또는 'name' 타입 지정
let a: PersonKeys = 'age';// 'age2' 불가
let b: PersonKeys = 'name';
interface Person {
[key: string]: number;
}
type PersonKeys = keyof Person;
// string | number
// ? key를 string으로 지정해뒀는데..
// obj 키에 number 넣어도 자동으로 스트링으로 변환되기 때문에
// 아래 타입을 모두 string 타입으로 바꾸고 싶을 때?
type Car = {
color: boolean;
model: boolean;
price: boolean | number;
};
// 변환기 만들기
type TypeChanger<MyType> = {
[key in keyof MyType]: string;
};
type NewType = TypeChanger<Car>;
let a: NewType = {
color: false, // 에러
};
[key in keyof MyType] : string
keyof MyType // 'color' | 'model' | 'price'
키값이 오른쪽 유니온 타입에 포함된다면 string으로 타입지정해주세요!
type Bus = {
color : string,
model : boolean,
price : number
}
type TypeChanger <MyType, T> = {
[key in keyof MyType]: T;
};
type NewBus = TypeChanger<Bus, boolean>;
type NewBus2 = TypeChanger<Bus, string[]>
type Age<T> = T extends string ? T : unknown;
// extends : T 가 string 타입인지 확인
let a: Age<string>; // string 타입
let b: Age<number>; // unknown 타입
// 파라미터로 array 타입 입력하면 array의 첫 인덱스 타입을
// 다른 거 입력하면 any를 타입으로.
type FisrtItem<T> = T extends any[] ? T[0] : any;
let c: FisrtItem<string[]> = '스트링';
let d: FisrtItem<number>; //any
// 기존 : T가 스트링타입이면 T를 아니면 unknown으로 타입지정
type Person<T> = T extends string ? T : unknown;
// infer
type Person2<T> = T extends infer R ? R : unknown;
// T에서 타입을 추출해라
let a: Person2<string>;
// 실용예제- array 내부 타입 추출
type 타입추출<T> = T extends (infer R)[] ? R : unknown;
type a = 타입추출<string[]>;
// T는 string[]으로 들어왔고 R[]이 T를 추출해서 R이 string이 됨.
// a의 타입이 string이 됨.
// 예제2 - 함수 추출
type 타입추출2<T> = T extends () => infer R ? R : unknown;
type b = 타입추출2<() => void>;
// b 타입 void
이거 써도 됨! // #### ReturnType type c = ReturnType<() => void>;
1번
- array 타입 입력 -> array 첫 자료가 string이면 string타입
-> 아니면 unknowntype 추출<T> = T extends [string, ...any] ? T[0] : unknown; let age1: 추출<[string, number]>; // string let age2: 추출<[boolean, number]>; // unknown
2번
- 함수의 파라미터 타입을 추출
type 추출<T> = T extends (x: infer R) => any ? R : any; let a: 추출<(x: number) => void>; // number let b: 추출<(x: string) => void>; // string