
?(객체타입을 지정할 때), readonly(읽기 전용 속성)type UserA = {
name: string;
readonly age: number; //읽기만 가능한! 쓰기 불가능!
isValid?: boolean
};
interface UserB {
name: string,
readonly age: number
isValid?: boolean //물음표를 붙이면 속성이 필수가 아닌 선택이 된다. 있어도 없어도 ok
}
const user: UserB = {
name: 'jth',
age: 25,
};
user.age = 80 // Error -> age는 읽기 전용 속성!!!
일반적으로 객체 데이터 타입을 정의할 때는
type,interface무엇을 사용해도 상관은 없지만interface를 더 사용하는 추세이다!
(매개변수 : 매개변수타입) : 반환 타입interface User {
name: string;
age?: number;
}
type GetUserNameT = (u: User) => string;
interface GetUserNameI {
(u: User): string; // 이러한 패턴이 호출 시그니처!!
}
const user: User = {
name: 'jth',
};
const getUserName: GetUserNameI = (user: User) => user.name;
const username = getUserName(user);
console.log(username); //jth
클래스 implements 지정할 타입interface UserI {
name: string;
getName(): string;
}
interface UserC {
new (n: string): UserI //생성(구문) 시그니처
}
class User implements UserI { //UserI는 User라는 클래스를 통해 만들어지는 인스턴스의 타입이다.
public name;
constructor(name: string) {
this.name = name;
}
getName() {
return this.name;
}
}
const user = new User('jth');
user.getName(); // 'jth'
function hello(userClass: UserC, msg: string) {
const user = new userClass('jth');
return `Hello ${user.getName()}, ${msg}`;
}
hello(User, 'good morning');
클래스에서 반환되는 것은 결국 인스턴스이기 때문에 생성(구문) 시그니처를 작성할 때 반환될 타입은 결국 인스턴스의 타입이다...
인덱싱 가능 -> 인덱스 시그니처(Index signature)
타입 부분과 console을 비교해보면 ex)[1: number] 이고 반환되는 값은 'B' -> string 값
배열
interface Arr {
[key: number]: string; //key부분은 원하는 이름 가능
}
const arr: Arr = ['A', 'B', 'C'];
console.log(arr[1]); //'B'
interface ArrLike {
[key: number]: string;
}
const arrLike: ArrLike = { 0: 'A', 1: 'B', 2: 'C' };
console.log(arrLike[1]); //'B'
interface Obj {
[key: string]: string;
}
const obj: Obj = { a: 'A', b: 'B', c: 'C' };
console.log(obj['b']); //'B'
console.log(obj.b); //'B'
꼭 있어야 하는 속성,
아직은 모르지만 추가될 수 있는 속성들을 인덱싱 가능 타입으로 만들어 줄 수 있다. 아래 코드에서name과age는 꼭 있어야 하는 속성에 해당한다.interface User { [key: string]: string | number; name: string; age: number; } const user: User = { name : 'jth', age: 25, email: 'abc@naver.com', city: '인천광역시', isValid: true // Error boolean 타입은 없음 }
interface User {
[key: string]: string | number; // 에러 해결을 위해 인덱스 시그니처 만들어주기
name: string;
age: number;
}
const user: User = {
name: 'jth',
age: 25,
};
console.log(user['age']); //25
interface PayLoad {
[key: string]: unknown; // 에러 해결을 위해 인덱스 시그니처 만들어주기
}
function getValues(payload: PayLoad) {
if (payload && payload.constructor === Object) {
return Object.keys(payload).map((key) => payload[key]);
}
}
getValues(user); //['jth',25]
interface User {
name: string,
age: number
}
const a: User['name'] = '123' //타입을 인덱싱하여 여긴 'string' 타입
const b: User['age'] = 123 //타입을 인덱싱하여 여긴 'number' 타입
interface UserA {
name: string;
age: number;
}
interface UserB extends UserA { // UserA의 속성이 확장되어 name, age 속성이 포함되어짐
isValid: boolean;
}
const user: UserB = {
name: 'jth',
age: 25,
isValid: true,
};
extends를 하지 않고 같은 interface를 사용하게 된다면 처음에 작성된 인터페이스에서 두번째 작성된 인터페이스가 합쳐져 둘의 속성을 모두 사용가능하다. 덮어쓰는 개념이 아니라 병합된다.interface User {
name: string;
age: number;
}
interface User {
isValid: boolean;
}
const user: User = {
name: 'jth',
age: 25,
isValid: true,
};
후속 속성 선언에 같은 타입이 존재해야한다.interface FullName {
firstName: string,
lastName: string // 후속 속성 선언 타입이 다름
}
interface FullName {
middleName: string,
lastName: boolean // Error
}
this를 사용하여 정의 가능하다.일반함수의 this는 호출되는 위치에서 정의된다.interface User {
name: string;
}
function greet(this: User, msg: string) { //여기
return `Hello ${this.name}, ${msg}`;
}
const jth = {
name: 'jth',
greet,
};
jth.greet('Good'); //'Hello jth, Good'
const neo = {
name: 'Neo',
};
greet.call(neo, 'Nice to meet you');
적재하다라는 의미function addString(x: string, y: string) {
console.log(x, y);
return x + y;
}
function addNumber(x: number, y: number) {
console.log(x, y);
return x + y;
}
function add(x: number, y: number) : number //선언부
function add(x: string, y: string) : string //선언부
function add(x: boolean, y: string[]) : string //선언부
function add(x: any, y: any): any { //구현부
console.log(x, y)
return x + y
}
add('Hello', 'World');
add('Hello', 123); //Error
add(123, 456);
add(123, 'World'); //Error
interface UserBase {
name: string;
age: number;
}
interface User extends UserBase { // 타입 1은 객체타입, 2는 문자열, 숫자 - > 오버로딩
updateInfo(newUser: UserBase): User;
updateInfo(name: string, age: number): User;
}
const user: User = {
name: 'jth',
age: 25,
updateInfo: function (nameOrUser: UserBase | string, age?: number) {
// 타입 2
if (typeof nameOrUser === 'string' && age !== undefined) {
this.name = nameOrUser;
this.age = age;
}
// 타입 1
else if (typeof nameOrUser === 'object') {
this.name = nameOrUser.name;
this.age = nameOrUser.age;
}
return this;
},
};
console.log(user.updateInfo({ name: 'hong', age: 30 }));
// {name : 'hong', age : 30, updateInfo : f}
console.log(user.updateInfo('neo', 10));
// {name : 'neo', age : 10, updateInfo : f}
public (생략 가능)
protected (해당 클래스와 확장된 하위 클래스에서만 접근 가능)
private (해당 클래스 내에서만 접근 가능)
static (정적메소드 접근 제어자 뒤에 작성!!!)
readonly (읽기 전용 최초 초기화 코드를 제외하고 새로 할당 불가능!)
✔readonly는 속성에만 적용하고 메소드에서는 사용 X
접근 제어자
abstract 키워드를 꼭 붙이자!interface와 다르게 타입 선언을 포함하고 구현부도 포함할 수 있다!abstract class AnimalA {
abstract color: string
abstract getColor(): string
constructor(public sound : string){}
speak() {return this.sound}
}
class Dog extends AnimalA {
constructor(
sound: string,
public color: string
){
super(sound) // 상위(슈퍼) 클래스가 있으므로 꼭 써주자!
}
getColor() {return this.color}
}
const dog = new Dog('멍멍', 'white')
dog.speak() //'멍멍'
dog.getColor() //'white'
extends로 명시하는 슈퍼클래스는 단일! 하나만 가능(Is-A 관계)하지만implements로 정의하는 interface는 여러 개 사용(Has-A 관계)이 가능하다.
extends 와 implements를 함께 사용할 수 있다.interface AnimalI1 {
sound: string
speak(): string
}
interface AnimalI2{
color: string
getColor(): string
}
abstract class CatA{
abstract name: string
}
class Cat extends CatA implements AnimalI1, AnimalI2 {
constructor(
public name: string,
public sound: string,
public color: string
){
super()
}
speak() {return this.sound}
getColor() {return this.color}
}
주의할 점은
extends키워드가 앞에 와야 하고슈퍼 클래스가 존재하므로 super()를 꼭 사용해주자! 추상 클래스와 interface는 상황에 맞게 적절하게 사용하는 법을 익히도록 하자!!!
- 클래스 간에 공통된 멤버 타입, 공통된 구현, 공통된 constructor함수가 있을 경우 추상 클래스가 유리할 수 있다.
- 여러 개의 클래스가 비슷하지만 다른 일부 구조를 가지고 있거나 각 클래스가 독립적으로 작동해야 할 때는 interface가 유용할 수 있다!!
정리가 잘 된 글이네요. 도움이 됐습니다.