interface Point {
x: number;
y: number;
}
function printPoint (p: Point) {
console.log(`${p.x}, ${p.y}`);
}
const point = {x: 12, y: 26}; //여기 변수는 Point 타입으로 선언 된 적이 없지만,
printPoint(point);
//TS는 타입 검사에서 point 와 Point 형태를 비교,
둘 다 같은 형태라 통과!
interface Point { 👈 여기의 속성이랑
x: number;
y: number;
}
function printPoint (p: Point) {
console.log(`${p.x}, ${p.y}`);
}
class VirtualPoint { 👈 여기의 속성이 같다
x: number;
y: number; // 모든 속성이 다 있음!
constructor(x: number, y: number){
this.x = x;
this.y = y;
}
}
const newVPoint = new VirtualPoint(13, 56);
printPoint (newVPoint);
잘 알려면 예제를 열심히 따라 쳐라
function printLabel(labelObj: { label: string }) { // 타입검사는 호출을 확인한다.
// printLabel는 string 타입 label을 갖는 객체를 매개변수로 갖는다.
console.log(labelObj.label);
}
let myObj = { size: 10, label: "Size 10 Object" };
// 컴파일러는 최소 필요한 프로퍼티가 있는지, 타입이 잘 맞는지만 검사를 한다.
printLabel(myObj);
// 👇 똑같은 예제를 interface로 바꿔보자
interface LabelValue { // 요구사항을 기술하는 이름으로 사용 가능
label:string;
}
function printLabel(labelObj: LabelValue) {
// 여기에 전달한 객체가 LabelValue 인터페이스를 구현 해야한다고 명시적으로 얘기 할 필요 없다.
console.log(labelObj.label);
}
let myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj);
interface SquareConfig {
color?: string;
width?: number;
// 선택적 프로퍼티는 선언할 때 이름 끝에 '?'를 붙여 표시한다.
}
function createSquare(config: SquareConfig):{color: string; area: number}{
let newSquare = {color: 'white', area: 100};
if (config.color) {
// 인터페이스에 속하지 않는 프로퍼티 사용을 방지한다
newSquare.color = config.color;
}
if (config.width){
newSquare.area = config.width * config.width;
}
return newSquare;
}
let mySquare = createSquare({color: 'black'});
readonly
를 넣어서 지정한다.Array<T>
와 동일한 ReadonlyArray<T>
타입이 있다.let a: number[] = [1,2,3,4];
let ro: ReadonlyArray<number> = a;
ro[0] = 12; // Index signature in type 'readonly number[]' only permits reading.(2542)
ro.push(5); // Property 'push' does not exist on type 'readonly number[]'.(2339)
ro.length = 100; // Cannot assign to 'length' because it is a read-only property.(2540)
a = ro; // The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.(4104)
//👆 ReadonlyArray를 일반 배열에 재할당이 불가능
a = ro as number[]; // ⭕타입 단언(type assertion)으로 오버라이드하는 것은 가능
const
VS readonly
변수는 const
프로퍼티는 readonly
interface SquareConfig {
color?: string;
width?:number;
}
function createSquare(config: SquareConfig): {color: string, area: number}{
let newSquare = {color: 'white', area: 100};
if(config.color) {
newSquare.color = config.color;
}
if(config.width) {
newSquare.area = config.width * config.width;
}
return newSquare;
}
let mySquare = createSquare({colour: 'black', width: 100}) // 이부분을 colour로 바꿈
// Argument of type '{ colour: string; width: number; }' is not assignable to parameter of type 'SquareConfig'
// Object literal may only specify known properties, but 'colour' does not exist in type 'SquareConfig'
// Did you mean to write 'color'?(2345)
JS 였으면 마지막 줄에서 조용히 오류가 난다.
width
는 있고,color
는 없고,colour
프로퍼티는 별로 안중요해서 이 프로그램이 올바르게 작성되었다고 생각 할 수도 있어. 하지만 우리의 TS는 용납하지 않지. 객체 리터럴은 다른 변수에 할당할 때, 인수로 전달할 때 특별한 처리를 받고, 초과 프로퍼티 검사를 받는다.
검사를 피하는 방법
let mySquare = createSquare({ width: 100, opacity: 0.5 } as SquareConfig);
SquareConfig
에 추가 프로퍼티가 있을거라고 확실하게 예상한다?interface SquareConfig {
color?: string;
width?:number;
⭐[propName : string]:any;
}
let squareOptions = { colour: "red", width: 100 }; //width가 공통 프로퍼티
let mySquare = createSquare(squareOptions);
간단하면 위의 방법들은 시도하지 말자.
더 복잡한 객체 리터럴에서 이 방법을 생각해볼 수 있다. 하지만 초과 프로퍼티 에러는 대부분 실제 버그다.
선택적 프로퍼티에서 이런 오류가 난다면 타입 정의를 수정해야 할 필요가 있다.
참조 : 함수
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc; // SearchFunc을 타입으로 갖는 변수
mySearch = function(source: string, subString: string) {
// 변수는 함수를 할당, string 타입 매개변수 두개랑 bool 타입 값을 리턴
let result = source.search(subString);
return result > -1;
}
mySearch = function(source: string, subString: string)
mySearch = function(src: string, sub: string) // 매개변수의 이름이 같을 필요는 없다!
mySearch = function(src, sub) // 타입이 정의되지 않음.
// 함수의 매개변수는 같은 위치에 대응대는 매개변수와 한 번에 하나씩 검사한다.
// SearchFunc 타입의 변수로 함수의 매개변수 값이 할당되기 때문에
// TS의 문맥상 타이핑 (contextual typing)이 자동으로 매개변수의 타입을 추론한다.
문자열
과 숫자
가 있다. interface StringArray {
[index: number]: string; // number로 인덱싱하면 string을 반환한다
}
let myArray: StringArray;
myArray = ['BOB', 'FRED'];
let myStr = myArray[0];
console.log(myStr);
interface NumberDitionary {
[index: string]: number;
length: number;
name: string; // 에러, 반환값이 string으로 맞지 않기때문이지
}
[index: string]: number | string;
Readonly
타입으로 만들 수 있다.interface ReadonlyStringArray {
readonly [index: number]: string;
}
let myArray: ReadonlyStringArray = ['ali', 'bob'];
myArray[2] = 'mell' // 오류! 읽기 전용이므로 변경 불가!
implements
를 사용한다.interface ClockInterface {
currentTime: Date;
}
class Clock implements ClockInterface {
currentTime: Date = new Date();
constructor(h: numer, m: number) {
}
}
interface ClockInterface {
currentTime: Date;
setTime(d: Date): void;
}
class Clock implements ClockInterface {
currentTime: Date = new Date();
setTime(d: Date) {
this.currentTime = d;
}
constructor(h: number, m: number){}
}
Animal cat = new Animal()
cat
은 객체이고 객체는 Animal
의 인스턴스이다. 객체(cat
)가 클래스(Animal
)의 객체인지를 관계위주로 설명할 때 사용static
이기 때문에 클래스의 인스턴스만 검사한다. interface ClockConstructor {
new (hour: number, minute: number);
}
class Clock implements ClockConstructor {
constructor(h: number, m: number) { }
}
interface ClockConstructor {
new (hour: number, minute: number): ClockInterface;
}
interface ClockInterface {
tick(): void;
}
function createClock(ctor: ClockConstructor, hour: number, minute: number): ClockInterface {
return new ctor(hour, minute);
}
class DigitalClock implements ClockInterface {
constructor(h: number, m: number){ }
tick() {
console.log('beep beep');
}
}
class AnalogClock implements ClockInterface {
constructor(h: number, m: number) {}
tick() {
console.log('tick tok');
}
}
let digital = createClock(DigitalClock, 12, 17);
let analog = createClock(AnalogClock, 7, 32);
// createClock 생성자 함수의 첫번째 매개변수 ctor은
// (hour: number, minute: number)의 생성자로 만들어진 인스턴스이며 이는
// tick() 메서드를 갖는 ClockInterface 타입
ClockConstructor
인터페이스를 할당하는 건 인스턴스여야 한다는 점이다.interface ClockConstructor {
new (hour: number, minute: number): ClockInterface;
}
interface ClockInterface {
tick(): void;
}
const Clock: ClockConstructor = class Clock implements ClockInterface {
constructor(h: number, m: number) {}
tick() {
console.log("beep beep");
}
};
let clock = new Clock(12, 17);
clock.tick();
interface Shape {
color: string;
}
interface Square extends Shape {
sideLength: number;
}
let square = {} as Square;
square.color = 'blue';
square.sideLength = 10;
interface Shape {
color: string;
}
interface PenStroke {
penWidth: number;
}
interface Square extends Shape, PenStroke {
sideLength: number;
}
let square = {} as Square;
square.color = 'blue';
square.sideLength = 10;
square.penWidth = 5.0;
interface Counter {
(start: number): string; // 매개변수
interval: number; // 프로퍼티
reset(): void; // 메서드
}
function getCounter(): Counter {
let counter = (function (start: number){}) as Counter;
counter.interval = 123;
counter.reset = function() { };
return counter;
}
let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;
private
과 protected
멤버도 상속받는다.private
과 protected
멤버 포함 클래스를 확장할 수 있다는 뜻.class Control {
private state: any;
}
interface SelectableControl extends Control {
select(): void;
// Control를 상속받음
// Control, 또는 그 밑에 클래스에 의해서만 구현 될 수 있다.
}
class Button extends Control implements SelectableControl {
select() { }
// 얘랑
}
class TextBox extends Control {
select() { }
// 얘
}
// 에러!
class Image implements SelectableControl {
private state: any;
select() { }
// 얘는 왜 안되냐?
// Control 클래스에 의해 만들어진 state여야만 하는데 (private이기 때문에)
// 이 클래스에서 새롭게 정의한 state여서 호환이 안됨.
}