TypeScript의 인터페이스로 객체 캐스트


94

Express의 요청 본문 (본문 파서 미들웨어 사용)에서 인터페이스로 내 코드를 캐스트하려고 시도하고 있지만 형식 안전성을 적용하지 않습니다.

이것은 내 인터페이스입니다.

export interface IToDoDto {
  description: string;
  status: boolean;
};

이것은 내가 캐스트를 시도하는 코드입니다.

@Post()
addToDo(@Response() res, @Request() req) {
  const toDo: IToDoDto = <IToDoDto> req.body; // <<< cast here
  this.toDoService.addToDo(toDo);
  return res.status(HttpStatus.CREATED).end();
}

마지막으로 호출되는 서비스 메서드 :

public addToDo(toDo: IToDoDto): void {
  toDo.id = this.idCounter;
  this.todos.push(toDo);
  this.idCounter++;
}

인터페이스 정의일치하지 않는 인수를 포함하여 모든 인수를 전달할 수 있으며이 코드는 정상적으로 작동합니다. 응답 본문에서 인터페이스로의 캐스트가 가능하지 않으면 Java 또는 C #과 같은 런타임에 예외가 발생합니다.

나는 TypeScript 캐스팅에는 존재하지 않고 Type Assertion 만 존재한다는 것을 읽었습니다. 그래서 컴파일러에게 객체가 유형임을 알려줍니다. x그래서 ... 내가 틀렸습니까? 형식 안전성을 강화하고 보장하는 올바른 방법은 무엇입니까?


1
"작동하지 않음"을 정의하십시오. 정확해야합니다. 오류가 있습니까? 어느 것? 컴파일 타임에? 런타임에? 무슨 일이야?
JB Nizet

1
런타임에 코드는 내가 전달하는 객체와 함께 정상적으로 실행됩니다.
Elias Garcia

당신이 요구하는지 분명하지 않다
Nitzan 토 메르에게

내 질문은 들어오는 객체를 유형이 지정된 객체로 캐스팅하는 방법입니다. 캐스트가 가능하지 않으면 런타임에 Java, C #과 같은 예외를 throw합니다.
Elias Garcia

이것이 귀하의 질문에 대답합니까? TypeScript 또는 JavaScript 유형 캐스팅
Michael Freidgeim

답변:


133

자바 스크립트에는 캐스팅이 없으므로 "캐스팅이 실패"하면 던질 수 없습니다.
Typescript 는 캐스팅을 지원 하지만 컴파일 시간에만 해당되며 다음과 같이 할 수 있습니다.

const toDo = <IToDoDto> req.body;
// or
const toDo = req.body as IToDoDto;

값이 유효하고 오류가 발생하지 않는 경우 런타임에 확인할 수 있습니다. 예 :

function isToDoDto(obj: any): obj is IToDoDto {
    return typeof obj.description === "string" && typeof obj.status === "boolean";
}

@Post()
addToDo(@Response() res, @Request() req) {
    if (!isToDoDto(req.body)) {
        throw new Error("invalid request");
    }

    const toDo = req.body as IToDoDto;
    this.toDoService.addToDo(toDo);
    return res.status(HttpStatus.CREATED).end();
}

편집하다

@huyz가 지적했듯이 isToDoDto유형 가드 이기 때문에 유형 어설 션이 필요하지 않으므로 충분해야합니다.

if (!isToDoDto(req.body)) {
    throw new Error("invalid request");
}

this.toDoService.addToDo(req.body);

const toDo = req.body as IToDoDto;TS 컴파일러 IToDoDto가이 시점에서 알고 있기 때문에 캐스트가 필요하지 않다고 생각합니다
huyz dec

9
일반적으로 유형 어설 션을 찾는 사람에게는 <>를 사용하지 마십시오. 이것은 더 이상 사용되지 않습니다. 사용as
Abhishek Deb

" javascript에는 캐스트가 없으므로"캐스팅이 실패 "하면 던질 수 없습니다. "요점은 TypeScript의 인터페이스가 실행 불가능하다고 생각합니다. 사실, 그것들은 100 % 합성 설탕 입니다. 그들은 쉽게 구조를 유지 할 수 있도록 개념적으로 ,하지만 transpiled 코드에 실제 영향이 없다 - OP의 질문 증거로, IMO, 굉장히 혼란 / 안티 패턴이다. 인터페이스와 일치하지 않는 것들이 트랜스 파일 된 자바 스크립트에 던져지지 않을 이유가 없습니다. TypeScript의 의식적인 (그리고 가난한 imo) 선택입니다.
ruffin

@ruffin 인터페이스는 통사론 적 설탕은 아니지만 런타임에만 유지하도록 의식적으로 선택했습니다. 나는 그것이 훌륭한 선택이라고 생각한다. 그렇게하면 런타임에 성능 저하가 없다.
Nitzan Tomer

Tomayto tomahto ? TypeScript의 인터페이스의 유형 안전성은 트랜스 파일 된 코드로 확장되지 않으며 사전 런타임에서도 유형 안전성이 심각하게 제한됩니다. OP 문제에서 볼 수 있습니다. 이 전혀없는 에서 됩니다 . TS는 "잠깐만 요. 아직 any보장되지 않습니다 IToDoDto!" 라고 말할 수 있지만 TS는 그렇게하지 않았습니다. 컴파일러가 일부 유형 충돌 만 포착 하고 트랜스 파일 된 코드에서 아무것도 포착 하지 못한다면 (그리고 당신이 옳습니다. 원본보다 더 명확해야했습니다), 인터페이스는 불행히도 imo, [대부분?] 설탕입니다.
Ruffin

7

다음은 TS 컴파일러가 일반적으로 불평하는 호환되지 않는 유형과 인터페이스 간에도 유형 캐스트를 강제하는 또 다른 방법입니다.

export function forceCast<T>(input: any): T {

  // ... do runtime checks here

  // @ts-ignore <-- forces TS compiler to compile this as-is
  return input;
}

그런 다음이를 사용하여 캐스트 오브젝트를 특정 유형으로 강제 지정할 수 있습니다.

import { forceCast } from './forceCast';

const randomObject: any = {};
const typedObject = forceCast<IToDoDto>(randomObject);

복잡성을 줄이기 위해 캐스팅하기 전에 런타임 검사를 수행해야하는 부분은 생략했습니다. 내 프로젝트에서 내가하는 일은 모든 .d.ts인터페이스 파일을 JSON 스키마로 컴파일 ajv하고 런타임에서 유효성을 검사 하는 데 사용 하는 것입니다.


1

누구에게나 도움이된다면 비슷한 인터페이스를 가진 다른 유형으로 객체를 취급하고 싶었던 문제가있었습니다. 나는 다음을 시도했다 :

보풀을 통과하지 못함

const x = new Obj(a as b);

linter는 a존재하는 속성이 누락되었다고 불평했습니다 .b . 즉,의 a일부 속성과 메서드가 b있지만 전부는 아닙니다. 이 문제를 해결하기 위해 VS Code의 제안을 따랐습니다.

Linting 및 테스트 통과

const x = new Obj(a as unknown as b);

코드 b에서 type a에서 구현되지 않은 유형에있는 속성 중 하나를 호출하려고 하면 런타임 오류가 발생해야합니다.


1
이 답변을 찾아서 다행입니다.하지만 네트워크 나 다른 앱을 통해 'x'를 보내는 경우 개인 정보가 유출 될 수 있습니다 (예 : 'a'가 사용자 인 경우). 'a'의 모든 속성이 있으며 typescript에는 사용할 수 없습니다.
Zoltán Matók

@ ZoltánMatók 좋은 지적입니다. 또한 네트워크를 통해 직렬화 된 객체를 보내는 것과 관련 하여 JavaScript 및 메서드에 대한 Java 스타일 getter 및 setter에 대한 인수 가 있습니다. getset
Jason
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.