class Department {
name: string;
// 생성자
constructor(n: string) {
this.name = n;
}
}
const accounting = new Department('Accounting');
class Department {
name: string;
constructor(n: string) {
this.name = n;
}
describe() {
console.log('Department: ' + this.name);
}
}
const accounting = new Department('Accounting');
const accountingCopy = { describe: accounting.describe };
accountingCopy.describe(); // Department: undefined
위 처럼 하게 되면 name properties를 갖고있지 않기 때문에 undefined가 뜨게 됨
class Department {
name: string;
constructor(n: string) {
this.name = n;
}
describe(this: Department) {
console.log('Department: ' + this.name);
}
}
const accounting = new Department('Accounting');
const accountingCopy = { describe: accounting.describe };
accountingCopy.describe(); // ERROR
함수에 class 인자를 넘겨 받아 해결하면 되지만 여전히 에러 뜨게 됨
class Department {
name: string;
constructor(n: string) {
this.name = n;
}
describe(this: Department) {
console.log('Department: ' + this.name);
}
}
const accounting = new Department('Accounting');
const accountingCopy = { name: 'DUMMY', describe: accounting.describe };
accountingCopy.describe(); // Department: DUMMY
함수 호출에 필요한 Department의 요소를 입력해주게 되면 에러 해결 됨
JavaScript에서는 default가 public, TypeScript에서 private 개념이 추가된 것임.
class Department { name: string; private employees: string[] = [];
addEmployee(employee: string) {
this.employees.push(employee);
}
}
const accounting = new Department('Accounting');
accounting.employees[2] = 'Anna'; // ERROR, Access private properties.
### \#
> ES10 이상에서만 사용 가능
```typescript
class Example {
private traditionalPrivateField: number;
#privateClassField: number;
constructor() {
this.traditionalPrivateField = 10;
this.#privateClassField = 20;
}
getTraditionalPrivateField() {
return this.traditionalPrivateField;
}
getPrivateClassField() {
return this.#privateClassField;
}
}
둘은 동일하게 매개변수는 class 외부에서 접근 불가능하고 내부에서만 사용가능하지만
private는 TypeScript에서 컴파일 과정에서만 에러를 나타내고 JavaScript에서 확인해보면
_traditionalPrivateField 로 명시만 바뀌고 접근이 가능해질 수 도 있는 상황이 된다.
따라서 #을 사용하여 런타임에서도 접근이 불가능하게 하는 것을 권장한다.
class Department {
private id: string;
private name: string;
private employees: string[] = [];
constructor(id: string, n: string) {
this.id = id;
this.name = n;
}
describe(this: Department) {
console.log('Department: ' + this.name);
}
}
위 생성자 코드를 더 간결하게 아래와 같이 Shorthand Initialization할 수 있음
class Department {
private employees: string[] = [];
constructor(private id: string, public name: string) { }
describe(this: Department) {
console.log('Department (${this.id}) : ' + this.name);
}
}
인자에 있는 public은 명시해줘야 단순히 입력받는 인자가 아니라 클래스에 추가하겠다는 의미기 때문에 명시해줘야 함.
class Department {
private employees: string[] = [];
constructor(private readonly id: string, public name: string) { }
}
읽기만 가능함
// 기본 클래스
class Actor {
name: string;
constructor(name: string) {
this.name = name;
}
actorName() {
return `배우의 이름은 ${this.name}`;
}
}
// 파생 클래스
class Person extends Actor {
constructor(name: string) {
super(name);
}
personName() {
return `${super.actorName()} 사람의 이름은 ${this.name}`;
}
}
const person = new Person('Jongseok');
console.log(person.personName()); // "배우의 이름은 Jongseok 사람의 이름은 Jonseok"
1. 매개변수로 Jongseok을 넣는다.
2. 파생 class의 생성자 함수로 Jongseok이 들어간다.
3. 생성자 함수에서 상위의 기본클래스인 name으로 들어간다. (super)
4. 기본 클래스의 name으로 들어간다.
5. 기본 클래스의 멤버로 세팅된다.객체의 구조
interface Person {
name: string;
age: number;
greet(pharse: string): void;
}
let user1: Person;
user1 = {
name: 'Max',
age: 30,
greet(pharse: string) {
console.log(pharse + ' ' + this.name);
}
};
user1.greet('Hi I\'m'); // Hi I'm Max
interface Person {
name: string;
age: number;
greet(pharse: string): void;
}
type Person = {
name: string;
age: number;
greet(pharse: string): void;
}
둘 다 위 예제를 적용시켜보면 오류 없이 동일하게 작동하지만
interface는 class와 함께 사용하기 적합하고 확장성을 갖고 있지만 interface는 객체를 정의해야하기 때문에 tuple, union, literal등의 타입은 지원하지 않는다.
type은 복잡한 타입을 정의할 수 있지만 확장성을 갖고 있지 않다.
interface의 확장성
interface Greetable {
name: string;
greet(pharse: string): void;
}
class Person implements Greetable {
name: string;
age = 30;
constructor (n: string) {
this.name = n;
}
greet(pharse: string) {
console.log(pharse + ' ' + this.name);
}
}
추상 클래스와 유사하게 작동함
interface Greetable {
name: string;
hobby?: number; // '?' => optional properties, 없어도 됨
greet(pharse: string): void;
}
class Person implements Greetable {
name: string;
age = 30;
constructor (n: string) {
this.name = n;
}
greet(pharse: string) {
console.log(pharse + ' ' + this.name);
}
}
let user1: Greetable; // user1: Person과 동일, 하지만 interface의 요소를 필요함을 명시
user1 = new Person('Max');
user1.greet('Hi'); // Hi Max
console.log(user1); // Person {age: 30, name: 'Max'}
user1 유형을 Person으로 해도 되지만 Person이 Greetable의 확장단계이기 때문에 user1: Greetable;을 할 수 있음
특정 클래스가 반드시 가져야 하는 속성과 메소드를 명시적으로 정의할 수 있음
interface Named {
readonly name: string; // private, public은 안되지만 readonly 가능
}
interface Greetable{
greet(pharse: string): void;
}
class Person implements Named, Greetable {
name: string;
age = 30;
constructor (n: string) {
this.name = n;
}
greet(pharse: string) {
console.log(pharse + ' ' + this.name);
}
}
위와 같이 여러 interface를 적용할 수 있지만
interface Named {
readonly name: string;
}
interface Greetable extands Named {
greet(pharse: string): void;
}
class Person implements Greetable {
name: string;
age = 30;
constructor (n: string) {
this.name = n;
}
greet(pharse: string) {
console.log(pharse + ' ' + this.name);
}
}
위 코드와 같이 확장할 수 있음.
추가적으로, TS에서 class는 한 클래스에 한 부모 클래스 즉, 다중 상속을 지원하지 않지만, interface에서는 다중 상속을 지원함
만약 인터페이스 다중 상속간에 동일한 이름의 메서드가 존재하고 코드가 다르다면 에러를 발생시킴.
type AddFn = (a: number, b: number) => number;
let add: AddFn;
add = (a, b) => a + b;
// let add: AddFn = (a, b) => a + b; 과 동일
interface {
(a: number, b: number): number;
}
let add: AddFn = (a, b) => a + b;
둘 다 동일하지만 일반적으로 아래의 코드를 많이 사용.
type AddFn = (a: number, b: number) => number;
let add: AddFn = (a, b) => a + b;
interface Named {
readonly name?: string;
}
interface Greetable extands Named {
greet(pharse: string): void;
}
class Person implements Greetable {
name?: string; // interface에서 '?'를 사용했기 때문에 class에서도 해줌
age = 30;
constructor (n?: string) {
if (n) {
this.name = n;
}
}
greet(pharse: string) {
if (this.name) {
console.log(pharse + ' ' + this.name);
} else {
console.log('Hi');
}
}
}
let user1: Greetable;
user1 = new Person();
user1.greet('Hi I\'m'); // Hi
console.log(user1); // Person {age: 30}
interface, class, properties에 Optional '?'을 추가하여 위와 같은 결과를 얻을 수 도 있음
큰 차이는 없지만 Type이 장점이 좀 더 많고,
주로 컨밴션으로 자리 잡은 것은 Type은 함수, Interface는 객체를 정의할 때 사용 함.