라이브러리 + TS를 조합해 사용할 시 효과적
→ 타입 정보가 그대로 유지되며 타입 흐름이 지속적으로 전달되므로
cf) 루프를 구현하면 타입 체크에 대한 관리도 직접 해야 함
csv 데이터 파싱 예제(JS)
vanilla JS : 절차형 프로그래밍으로 구현 가능
const csvData = '...';
const rawRows = csvData.split('\n');
const headers = rawRows[0].split(',');
const rows = rawRows.slice(1).map((rowStr) => {
const row = {};
rowStr.split(',').forEach((val, j) => {
row[headers[j]] = val;
});
return row;
});
함수형 프로그래밍 선호 개발자 : reduce
사용 → 행 객체 생성
const rows = rawRows
.slice(1)
.map((rowStr) =>
rowStr
.split(',')
.reduce((row, val, i) => ((row[headers[i]] = val), row), {})
);
키와 값의 배열로 취합해 객체로 만드는 zipObject
함수 이용
import _ from 'lodash';
const rows = rawRows
.slice(1)
.map((rowStr) => _.zipObject(headers, rowStr.split(',')));
→ JS : 서드 파티 라이브러리 종속성을 추가할 때 신중해야 함
→ 코드를 짧게 줄이는 데 시간이 많이 들 경우, 사용 지양할 것
→ TS : 서드파티 라이브러리 사용이 무조건 유리
→ 타입 정보를 참고해 작업할 수 있으므로, 서드파티 라이브러리 기반으로 변형 시 시간 단축
// CSV 파서 절차형 & 함수형 버전 모두 같은 오류 발생
const rowsA = rawRows.slice(1).map((rowStr) => {
const row = {};
rowStr.split(',').forEach((val, j) => {
row[headers[j]] = val;
// ~ '{}' 형식에서 'string' 형식의 매개변수가 포함된 인덱스 시그니처를 찾을 수 없습니다.
});
return row;
});
const rowsB = rawRows.slice(1).map(
(rowStr) =>
rowStr
.split(',')
.reduce((row, val, i) => row[((headers[i] = val), row)], {})
// ~ '{}' 형식에서 'string' 형식의 매개변수가 포함된 인덱스 시그니처를 찾을 수 없습니다.
);
→ 두 버전 모두 {}
타입으로 {[column: string]: string}
or Record<string, string>
제공 시 오류 해결
const rows = rawRows
.slice(1)
.map((rowStr) => _.zipObject(headers, rowStr.split(',')));
// 타입 : _.Dictionary<string>[]
→ 타입 구문 없이도 rows
의 타입이 정확함
데이터의 가공이 정교해질수록 장점은 더욱 분명해짐
p. 150 ~ 153 NBA 팀 명단 가공 예제 확인
내장된 함수형 기법과 로대시를 비롯한 라이브러리에 타입 정보가 잘 유지되는 이유
→ 함수 호출 시 전달된 매개변수 값을 건드리지 않고 매번 새로운 값을 반환해 새로운 타입으로 안전하게 반환 가능