Menggunakan properti kelas privat untuk memperkuat pengetikan dalam naskah ketikan

Inilah yang saya suka tentang naskah ketikan karena itu membuat saya tidak mengolok-olok omong kosong. Ukur panjang nilai numerik, dll. Pada awalnya, tentu saja, saya meludah, marah karena saya direcoki dengan segala macam formalitas bodoh. Tapi kemudian saya terlibat, jatuh cinta lebih keras. Nah, dalam arti sedikit lebih ketat. Saya mengaktifkan opsi strictNullChecks dalam proyek dan menghabiskan tiga hari untuk memperbaiki kesalahan yang muncul. Dan kemudian dia bersukacita dengan kepuasan, mencatat betapa mudah dan tidak terbatasnya refactoring sekarang.





Tapi kemudian Anda menginginkan sesuatu yang lebih. Dan di sini naskah ketikan perlu menjelaskan batasan apa yang Anda terapkan pada diri Anda sendiri, dan Anda mendelegasikan kepadanya tanggung jawab untuk memantau kepatuhan terhadap batasan ini. Ayo, hancurkan aku sepenuhnya.





Contoh 1

Beberapa waktu lalu, saya terjebak dengan ide menggunakan react sebagai mesin template di server. Tertangkap tentu saja oleh kemungkinan mengetik. Ya, ada bermacam-macam pesek, kumis dan apa lagi disana. Tetapi pengembang harus mengingat dirinya sendiri apakah dia lupa untuk memperluas argumen yang diteruskan ke templat dengan bidang baru. (Jika tidak demikian, perbaiki saya. Tetapi secara umum, saya tidak peduli - alhamdulillah saya tidak harus berurusan dengan pembuatan templat berdasarkan sifat pekerjaan saya. Dan contoh tentang hal lain).





Dan di sini kita biasanya dapat mengetikkan props yang diteruskan ke komponen, dan mendapatkan petunjuk IDE yang sesuai saat mengedit template. Tapi ini ada di dalam komponen. Sekarang mari kita pastikan bahwa kita tidak mentransfer aliran kiri apapun ke komponen ini.





import { createElement, FunctionComponent, ComponentClass } from 'react';
import { renderToStaticMarkup } from 'react-dom/server';

export class Rendered<P> extends String {
  constructor(component: FunctionComponent<P> | ComponentClass<P>, props: P) {
    super('<!DOCTYPE html>' + renderToStaticMarkup(
      createElement(component, props),
    ));
  }
}
      
      



Sekarang, jika kita mencoba mentransfer props dari order ke komponen pengguna, kita akan segera diberitahu tentang kesalahpahaman ini. Keren? Keren.





Tapi ini pada saat pembuatan html. Bagaimana hal-hal berjalan dengan penggunaan selanjutnya? Karena hasil instantiating Rendered hanya berupa string, maka skrip tidak akan bersumpah, misalnya dengan konstruksi sebagai berikut:





const html: Rendered<SomeProps> = 'Typescript cannot into space';
      
      



Karenanya, jika kita menulis sesuatu seperti ini:





@Get()
public index(): Rendered<IHelloWorld> {
  return new Rendered(HelloWorldComponent, helloWorldProps);
}
      
      



ini tidak menjamin bahwa hasil kompilasi HelloWorldComponent akan dikembalikan dari metode ini .





, :)





export class Rendered<P> extends String {
	_props: P;
	constructor(component: FunctionComponent<P> | ComponentClass<P>, props: P)
...
      
      



'cannot into space' , _props. . - . - _props, js , .. "" .









Object.assign('cannot into space', {_props: 42})
      
      



, . .





export class Rendered<P> extends String {
  // @ts-ignore -       noUnusedParameters
  private readonly _props: P;
  constructor(component: FunctionComponent<P> | ComponentClass<P>, props: P)
...
      
      



Object.assign , .. Rendered



_props , .





, , , . , , . .





2

, , , . - -. . .





. , . .





, -, .





ApiResponse. - , .





export interface IApiResponse {
	readonly scenarioSuccess: boolean;
	readonly systemSuccess: boolean;
	readonly result: string | null;
	readonly error: string | null;
	readonly payload: string | null;
}

export class ApiResponse implements IApiResponse {
	constructor(
		public readonly scenarioSuccess: boolean,
		public readonly systemSuccess: boolean,
		public readonly result: string | null = null,
		public readonly error: string | null = null,
		public readonly payload: string | null = null,
	) {}
}
      
      



scenarioSuccess true. , ( ) - scenarioSuccess false. - systemSuccess false. / result/error. . , scenarioSuccess true error.





, ApiResponse , :





export class ScenarioSuccessResponse extends ApiResponse {
  constructor(result: string, payload: string | null = null) {
    super(true, true, result, null, payload);
  }
}
      
      



.





- ApiResponse, " " , . .





const SECRET_SYMBOL = Symbol('SECRET_SYMBOL');

export abstract class ApiResponse implements IApiResponse {
  // @ts-ignore
  private readonly [SECRET_SYMBOL]: unknown;
  
  constructor(
    public readonly scenarioSuccess: boolean,
    public readonly systemSuccess:   boolean,
    public readonly result:  string | null = null,
    public readonly error:   string | null = null,
    public readonly payload: string | null = null,
  ) {}
}
      
      



Rendered



_props, , , . "" . . ( , ?)





. , , any. .





Di sini Anda juga dapat melihat bahwa komunikasi antara komponen sistem harus dipisahkan melalui antarmuka. Tetapi sangat mungkin bagi pihak penerima untuk meresepkan bahwa ia mengharapkan IApiResponse , tetapi layanan dari lapisan logika domain tidak masalah, biarkan ia mengembalikan implementasi ApiResponse tertentu .





Nah ...

Saya harap materi ini menarik untuk Anda. Bagi beberapa orang, pendekatan ini mungkin tampak berlebihan, dan saya tidak mendorong setiap orang untuk segera menambahkan "penjaga" seperti itu ke dalam proyek mereka. Tapi saya harap Anda menemukan bahan pemikiran dalam artikel saya.





Terima kasih atas waktu Anda. Saya akan senang menerima kritik yang membangun.








All Articles