TypeScript'te nesneyi arayüze dönüştür


Express'te (body-parser ara katmanını kullanarak) bir isteğin gövdesinden bir arabirime kodumda dönüştürme yapmaya çalışıyorum, ancak bu tür güvenliğini zorlamıyor.

Bu benim arayüzüm:

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

İşte dönüşümü yapmaya çalıştığım kod:

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

Ve son olarak, çağrılan servis metodu:

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

Herhangi bir argümanı geçirebilirim, arayüz tanımıyla hemen hemen hiç eşleşmeyenler de dahil, ve bu kod sorunsuz çalışacaktır. Eğer yanıt gövdesinden arayüze dönüşüm mümkün değilse, Java veya C#'ta olduğu gibi çalışma zamanında bir istisna fırlatılmasını beklerdim.

TypeScript'te casting olmadığını, sadece Type Assertion olduğunu okudum, bu yüzden bu sadece derleyiciye bir nesnenin x türünde olduğunu söyleyecek, yani... Yanılıyor muyum? Tür güvenliğini zorlamak ve sağlamak için doğru yol nedir?

Cevap #1

Javascript'te tip dönüştürme (casting) yoktur, bu yüzden "dönüştürme başarısız olursa" fırlatamazsınız.
Typescript tip dönüştürmeyi destekler ancak bu yalnızca derleme zamanı içindir ve bunu şu şekilde yapabilirsiniz:

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

Değerin geçerli olup olmadığını çalışma zamanında kontrol edebilir ve geçerli değilse hata fırlatabilirsiniz, yani:

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();
}

Düzenle

Dediği gibi @huyz, isToDoDto bir tür koruyucu olduğu için tür dönüşümüne gerek yok, bu nedenle bu yeterli olmalı:

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

this.toDoService.addToDo(req.body);

Cevap #2

TS derleyicisinin genellikle hata verdiği uyumsuz türler ve arayüzler arasında bile bir tür dönüşümünü zorlamanın başka bir yolu işte burada:

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

  // ... do runtime checks here

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

Sonra bunu, nesneleri belirli bir türe zorla dönüştürmek için kullanabilirsin:

import { forceCast } from './forceCast';

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

Karmaşıklığı azaltmak için dönüştürmeden önce çalıştırma zamanı kontrolleri yapmanız gereken kısmı çıkardığımı unutmayın. Projemde yaptığım şey, tüm .d.ts arayüz dosyalarımı JSON şemalarına derlemek ve ajv kullanarak çalıştırma zamanında doğrulamak.

Cevap #3

Eğer birine yardımcı olursa, ben, benzer bir arayüze sahip başka bir tür gibi bir nesneyi ele almak istediğim bir sorun yaşıyordum. Şunu denedim:

Lint kontrolünden geçmedi

const x = new Obj(a as b);

Linter, a'nın b'de bulunan özelliklerden eksik olduğunu belirtiyordu. Başka bir deyişle, a, b'nin bazı özelliklerine ve yöntemlerine sahipti, ama hepsine değil. Bunu aşmak için, VS Code'un önerisini takip ettim:

Lint kontrolünden ve testlerden geçti

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

Kodunuz, tür a'da uygulanmamış olan tür b'de var olan özelliklerden birini çağırmaya çalışırsa, bir çalışma zamanı hatası alacağınızı unutmayın.

İlgili makaleler