타입스크립트 클래스의 기본형태는 자바스크립트와 같다. 따라서 자바스크립트와의 차별점을 정리했다.
// 클래스<T> 형태로 제네릭 클래스 만들기
class Cup<T> {
public Content!: T; // 무조건 할당되어 있다고 컴파일러에게 알림 (생성자 null오류 방지)
}
let text = new Cup<string>();
text.Content = "문자열";
let integer = new Cup<number>();
integer.Content = 1_234;
log(`${text.Content}, ${integer.Content}`); // 문자열, 1234
// private과 public 접근
namespace FieldInitailizer {
class Say {
private message: string = "안녕하세요";
public hi(): void {
this.message = "반갑습니다.";
console.log(this.message);
}
}
const say = new Say();
// say.message = "또 만나요"; // private멤버 변수에 접근하면 에러발생
say.hi(); // 반갑습니다.
}
타입스크립트의 생성자에의 매개변수로 private이나 public접근 제한자를 앞에 붙여주면 필드 또는 속성이 자동으로 만들어진다. 이 때 외부 접근이 제한된 private변수는 필드라 칭하고, 접근 가능한 public은 속성이라 칭한다.
// private을 매개변수로 쓰면 필드가 자동으로 만들어진다.
// 아래 nameCard와 nameCard1은 동일하다.
class nameCard {
// 생성자에서 값을 바당서 자동으로 private name 필드를 생성
constructor(private name: string) {}
}
class nameCard1 {
name: string;
constructor(name: string) {
this.name = name;
}
}
let my = new nameCard("홍길동");
console.log(my.display()); // 이름 : 홍길동
let my = new nameCard1("고길동");
console.log(my.display()); // 이름 : 홍길동
// public도 마찬가지로 매개변수로 쓰면 속성이 자동으로 만들어진다.
class nameCard {
constructor(public name: string) { // Empty }
display() {
return `이름: ${this.name}`;
}
}
let my = new nameCard("빌게이츠");
console.log(my.display()); // 빌게이츠
my.name = "와우";
console.log(my.display()); // 와우
// 이렇게 public이 쓰인 속성은 외부에서 변경할 수 있다.
const log = console.log;
// Rest Parameter
function sumAll(...numbers: number[]) {
let sum: number = 0;
for (let i = 0; i < numbers.length; i++) {
sum += numbers[i];
}
return sum;
}
log(sumAll(3, 5)); // 8
log(sumAll(3, 5, 7)); // 15
log(sumAll(3, 5, 7, 9)); // 24
const log = console.log;
// 속성 만들고 사용하기
class Developer {
public name!: string; // 단순 set및 get;
}
// 클래스의 인스턴스 생성
const developer = new Developer();
// 속성에 값 설정 (set)세터
developer.name = "지영준";
// 속성의 값 조회 (get)게터
console.log(developer.name);
// 전체 속성 사용하기
class Person {
// 필드
private _name!: string;
// 속성
get name(): string {
return this._name; // 추가적 작업 가능
}
set name(value: string) {
this._name = value; // 추가적 작업 가능
}
}
let person = new Person();
person.name = "wow";
console.log(person.name);
class Point {
// 속성
public X!: number;
public Y!: number;
// 메서드
Draw(): void {
console.log(`X : ${this.X}, Y : ${this.Y}`);
}
}
let point = new Point();
point.X = 100;
point.Y = 200;
point.Draw();
class Employee {
// age 필드
private _age!: number;
// age 속성
get age() {
return this._age;
}
set age(value: number) {
if (value > 0 && value < 150) {
this.age = value;
} else {
throw "나이 값이 잘못되었습니다.";
}
}
}
const emp: Employee = new Employee();
emp.age = 150;
console.log(emp.age);
Static 맴버 : class내부의 static맴버는 공유 맴버다. 선언과 동시에 할당되기 때문에 클래스.스태틱맴버 이런식으로 접근할 수 있다.
class Car {
static copyright: string = "test1";
constructor(public name: string, public maker: string) {}
}
console.log(Car.copyright);
class Parent {
hi() {
console.log("부모하이");
}
}
class Child extends Parent {
hello() {
console.log("자식헬로");
}
}
let child = new Child();
child.hi(); // 부모클래스의 맴버
child.hello(); // 자식클래스의 맴버
// 추상클래스
abstract class Shape {
abstract getArea(): number;
}
// 원 클래스
class Circle extends Shape {
constructor(public radius: number) {
super();
}
getArea(): number {
return Math.PI * this.radius ** 2;
}
}
// 직사각형 클래스
class Rectangle extends Shape {
constructor(public w: number, public h: number) {
super();
}
getArea(): number {
return this.w * this.h;
}
}
// 정사각형 클래스
class Square extends Shape {
constructor(public size: number) {
super();
}
getArea(): number {
return this.size ** 2;
}
}
const circle = new Circle(10);
log(circle.getArea()); // 314.1592
const rectangle = new Rectangle(20, 10);
log(rectangle.getArea()); // 200
const squar = new Square(10);
const area = squar.getArea();
log(area); // 100
인터페이스는 여러 맴버를 주는 엔티티 형식과 멤버를 강제할 수 있는 규약을 정의한다.
인터페이스는 클래스에 포함될 수 있는 속성 또는 메서드에 대한 표준 규약을 제공한다.
interface ICar {
go(): void; // 함수 시그니처만 제공
}
class Car implements ICar {
go(): void {
log("인터페이스에 정의된 모든 맴버를 반드시 구현해야 합니다.");
}
}
let car = new Car();
car.go(); // '인터페이스에 정의된 모든 맴버를 반드시 구현해야 합니다.'
interface IContact {
name: string;
age: number;
}
let theContact = { name: "지영준", age: "29" };
function prointContact(contact: IContact): string {
return contact.name + contact.age;
}
log(prointContact(theContact));
// age를 문자열로 할당하면 에러 발생
// '{ name: string; age: string; }' 형식의 인수는 'IContact' 형식의 매개 변수에 할당될 수 없습니다.
// 'age' 속성의 형식이 호환되지 않습니다.
// 'string' 형식은 'number' 형식에 할당할 수 없습니다.ts(2345)
//-------------------------------------------------------------------------
interface IContact {
name: string;
age: number;
adrr: string;
}
let theContact = { name: "지영준", age: 29 };
function prointContact(contact: IContact): string {
return contact.name + contact.age;
}
log(prointContact(theContact));
// theContact에 adrr이 없어서 에러 발생
// '{ name: string; age: number; }' 형식의 인수는 'IContact' 형식의 매개 변수에 할당될 수 없습니다.
// 'adrr' 속성이 '{ name: string; age: number; }' 형식에 없지만 'IContact' 형식에서 필수입니다.ts(2345)
//-------------------------------------------------------------------------
interface IContact {
name: string;
age: number;
addr?: string; // Nullable옵션
}
let theContact = { name: "지영준", age: 29 };
function prointContact(contact: IContact): string {
if (contact.addr) return contact.name + contact.age + contact.addr;
else return contact.name + contact.age;
}
log(prointContact(theContact)); // 지영준29
//-------------------------------------------------------------------------
// 여러가지 정의 방법
// Entitiy형 정의
// Entitiy형 정의
interface IContact {
name: string;
age: number;
addr?: string; // Nullable옵션
}
let theContact = { name: "지영준", age: 29 };
let theContact2 = { name: "홍길동", age: 25 };
let theContact3 = { name: "김철수", age: 15 };
function prointContact(contact: IContact): string {
if (contact.addr) return contact.name + contact.age + contact.addr;
else return contact.name + contact.age;
}
// 함수형 정의
interface IPrintContact {
(contact: IContact): string;
}
// 반드시 IPrintContact에 따른 매개변수를 구현해야만 한다.
const contactPrinter: IPrintContact = (contact: IContact): string => {
return contact.name + contact.age;
};
// 배열형 정의
interface IContactArray {
[idx: number]: IContact;
}
let contactArray: IContactArray;
contactArray = [theContact, theContact2, theContact3];
log(prointContact(contactArray[2])); // 김철수15
// 클래스를 사용하여 묶어서 관리
interface IprintCurrentContact {
print(): string;
}
class Contact implements IContact, IprintCurrentContact {
name: string;
age: number;
addr: string;
constructor() {
this.name = "";
this.age = 0;
this.addr = "";
}
print(): string {
return this.name + this.age;
}
}
const contactTest = new Contact();
contactTest.name = "백두산";
contactTest.age = 100;
log(contactTest.print()); // 백두산100
// 클래스 상속
class ContactWithAddr extends Contact {
addr: string;
constructor() {
super();
this.addr = "";
}
print(): string {
return this.name + this.age + this.addr;
}
}
const contactTest2 = new ContactWithAddr();
contactTest2.name = "임꺽정";
contactTest2.age = 400;
contactTest2.addr = "조선";
log(contactTest2.print()); // 임꺽정400조선
type MultiString = [string, string];
let hello: MultiString = ["hello", "hello2"];
type Multi = [string, number];
let pi: Multi = ["PI", 3.14];
// call signature 방식으로 다형성 구현
// type SuperPrint = {
// (arr: number[]):void;
// (arr: boolean[]): void;
// (arr: string[]): void;
// }
// number boolean string 등 concrete type이 뭐가 들어올지 모를 때 제네릭을 사용한다.
type SuperPrint = {
<T>(arr: T[]): T;
};
const superPrint: SuperPrint = (arr) => arr[0];
const a = superPrint([true, false, true]);
// const superPrint: <boolean>(arr: boolean[]) => void
const b = superPrint([1, 2, 3, 4]);
// const superPrint: <number>(arr: number[]) => void
const c = superPrint(["a", "b", "c", "d"]);
// const superPrint: <string>(arr: string[]) => void
const d = superPrint([1, true, "c", "d"]);
// const superPrint: <string | number | boolean>(arr: (string | number | boolean)[]) => void
// const d: string | number | boolean
이 코드는 일반적인 방법은 아니다.
보통 제네릭은 라이브러리를 만들 때 할용한다. 따라서 라이브러리를 만들거나 다른 개발자가 사용할 기능을 개발하는 경우엔 제네릭이 유용할 것이다.
function superPrint<T>(a : T[]) {
return a[0];
}
const a = superPrint([true, false, true]);
// const superPrint: <boolean>(arr: boolean[]) => void
const b = superPrint([1, 2, 3, 4]);
// const superPrint: <number>(arr: number[]) => void
const c = superPrint(["a", "b", "c", "d"]);
// const superPrint: <string>(arr: string[]) => void
const d = superPrint([1, true, "c", "d"]);
// const superPrint: <string | number | boolean>(arr: (string | number | boolean)[]) => void
// const d: string | number | boolean
// 제네릭은 아래와 같이 재사용 할 수 있다.
type Player<E> = {
name: string;
extraInfo: E;
};
type NicoExtra = {
favFood: string;
};
type NicoPlayer = Player<NicoExtra>;
const nico: NicoPlayer = {
name: "nico",
extraInfo: {
favFood: "kimchi",
},
};
type A = Array<number>;
let a:A = [1,2,3,4];