- 매개변수 타입 설정: 필수, 생략 시 오류발생
- 함수 return타입 설정: 생략 가능, return값이 없는 단순 실행함수의 타입은 void
- 함수 실행 시 매개변수 타입, 개수, 순서와 일치하지 않는 값을 넣으면 오류가 발생
- 함수의 return타입은 적지 않아도 ts가 알아서 유추함
- function함수 이름(매개변수: 타입, 매개변수: 타입): return타입 {}
function addNum(a:number, b:number): number {
return a + b;
}
addNum(3,5);
addNum(3,"오"); //불가능
- 리턴값이 없는 경우
function sayHello(): void {
console.log("hello");
}
sayHello();
- interface 중괄호 내부에 key를 함수이름(매개변수), return타입은 :뒤에 작성하고 객체에서 사용할 때 함수 내용을 정의한다
- interface
interface Greet {
name: string;
hi(): string;
bye(a: number): string;
}
- 객체 jh를 Greet타입으로 생성
const jh: Greet = {
name: "allie",
hi() {
return "안녕 내 이름은 " + this.name + "이야";
},
bye(a: number) {
return `작별인사를 ${a}번 했습니다.`;
},
};
- 객체 내부의 데이터 사용할 때는 this.key이름으로 접근가능
- 항상 함수의 끝에 절대 도달하지 않는 경우에만 사용할 수 있다
- 조건에 따라 빠져나올 수 있으면 사용이 불가능하다
function goingOn(a: number): never {
while (true) {
console.log("끝나지 않는 함수입니다.");
// if (a > 10) break; 빠져나올 수 있는 조건이 추가되면 never타입 사용불가 빨간줄 생긴다
}
}
- 함수의 매개변수의 타입, 개수를 다르게 하고 이름은 동일하게 해서 비슷한 기능을 사용할 수 있도록 하는 방법
- ts의 오버로딩은 각각의 매개변수별 함수를 선언하고, any타입의 함수를 구현하여 사용하는 방법이 있다
- 예시
function addSomething(x: string, y: string): string;
function addSomething(x: number, y: number): number;
function addSomething(x: any, y: any) {
//구현은 any
return x + y;
}
addSomething(1, 2);
addSomething("안녕", "하세요");
- ts에서는 generic을 사용하는 방법을 권고하고 있다
- 실습문제
- 아래 처럼 전개연산자로 배열을 받으면 실행할 때마다 다른 개수의 배열을 받아 사용할 수 있다. 그냥 배열을 매개변수로 받으면 배열 한개만 받을 수 있다 알아두면 좋을 내용이라 다시 한번 복습해보았다
function sum2(...nums: Array<number>): number {
let sum = 0;
for (let num of nums) {
sum += num;
}
return sum;
}
console.log(sum2(1, 2, 3, 4, 10));
- generic은 타입을 정하지 않은 상태로 선언하고, 사용할 때 generic을 함께 전달하는 방식이다
- <제네릭>의 형태로 함수나 변수이름 바로 옆에 작성하고, 타입을 지정할 때 해당 제네릭 이름을 타입으로 입력한다
function printSome<T>(x: T, y: T): T {
console.log(x);
console.log(y);
return y;
}
//사용시 원하는 타입을 제네릭으로 전달
printSome<number>(1, 3);
printSome<string>("hi", "안녕");
//두 개 이상의 제네릭 타입도 사용가능
function printSome2<T, K>(x: T, y: K): void {
console.log(x);
console.log(y);
}
// T | boolean 처럼 유니언 타입으로 사용할 수도 있다
- 인터페이스이름<제네릭> 형태로 사용한다. key중에서 타입을 제네릭 타입으로 지정하는 식으로 사용할 수 있다
- 객체 생성시에 인터페이스타입<제네릭>으로 타입을 지정하고, 해당 키에 제네릭에 해당하는 데이터를 입력하면 된다
interface Phone<T> {
company: string;
createAt: Date;
option: T;
}
// T타입으로 string을 사용
const iphone15: Phone<string> = {
company: "apple",
createAt: new Date("2023-10-13"),
option: "black",
};
- 프로젝트 생성 시 ts를 사용하는 프로젝트라는 점을 명시해야한다
- npx create-react-app 프로젝트 이름 --template typescript
- props로 하위 컴포넌트가 데이터를 받으면 어떤 형태로 넘어오는지에 대한 interface가 반드시 존재해야한다
- props의 타입으로 인터페이스를 지정한다. function위에 쓰면 되지만 계속해서 사용하다보면 따로 파일을 만들어 export하는 것이 편하다
- 인터페이스로 정의된 props는 상위 컴포넌트에서 반드시!!! 넘겨줘야하며(에러발생) 선택적으로 전달하는 props의 경우 ?를 이용하여 인터페이스선언시 설정해주면 된다
- generic을 사용하지 않고 곧바로 원하는 초기값을 입력하더라도, 알아서 유추를 한다
- useState는 generic을 따로 사용하지 않아도 괜찮다
- state값이 null이거나 아닐때도 있으면 generic으로 반드시 union type을 전달해줘야한다
interface Data { name:string; age:number;}
const [data, setData] = useState<null | Data>(null)
- 변수 useRef: 초기값 타입을 generic으로 지정가능
- dom요소 useRef: generic에 해당 태그 타입을 적고 null로 초기화해야 한다. 타입은 여러종류가 있어 검색하면서 하면됨
const ref = useRef<HTMLInputElement>(null);
- event객체를 매개변수로 받을 때 타입은 여러가지이기 때문에 마찬가지로 검색 및 활용이 필요하다
- 태그에서 onClick= {(e)=> 함수내용} 형식으로 바로 접근할 때는 따로 타입을 작성할 필요가 없다
- 함수를 따로 선언 후 이벤트 요소를 받는다면 해당하는 이벤트 타입을 지정해줘야한다
- 일반적으로 React.이벤트이름<이벤트타겟요소>형태로 작성한다
const handleClick = (e: React.MouseEvent<HTMLElement>) => {
console.log(e);
console.log(e.target);
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
console.log(e.target);
console.log(e.target.value);
};
- 기존 jsx로 작성된 파일을 tsx로 변경하는 문제였는데 바꿀 부분은 거의 없었지만 은근히 헷갈리는 점이 많아 정리해보려고 한다
- 파일은 내 git링크의 30-typescript-react폴더의 practice에 있다
- PostList: 여기서는 가져오는 데이터 post의 인터페이스를 작성하고 state의 초기값에 generic을 추가해주는 작업을 했다
- 하위 컴포넌트에 post를 잘 전달하고 있어 그 부분은 따로 수정하지 않았다
- PostItem: 가져오는 props에 대한 인터페이스를 선언하고, 그 인터페이스에서 사용하는 post의 인터페이스도 복사 붙여넣기 해주었다(postlist의 post인터페이스 재사용)
- 헷갈린 부분은 props를 가져오는 과정에서 어떤 형태로 전달되는 것인지 혼동이 있었다
- 보면 위의 코드는 post라는 key로 Post인터페이스 형태의 배열 데이터를 가져오는 인터페이스 Props가 존재한다
- function의 posts는 Props인터페이스 형태(key=post, 내용은 Post배열)인 데이터를 담고 있다
- post.id, post.body로 바로 접근하기 위해 key인 {post}를 객체분해할당으로 가져와 실제로 뿌려주었다
- 즉, 정리하자면
const PostItem = (posts: Props) => {}
posts = 아무이름이나 가능
Props의 key = 상위컴포넌트에서 보내는 key이름과 동일
실제 데이터 접근 = posts배열안의 post값으로 실제 데이터 접근