Verwenden privater Klasseneigenschaften, um die Eingabe in Typoskript zu verstärken

Das ist es, was ich an Typoskript liebe, weil es mich davon abhält, Unsinn zu verprügeln. Messen Sie die Länge eines numerischen Werts usw. Zuerst spuckte ich natürlich, empört darüber, dass ich mit allerlei dummen Formalitäten belästigt wurde. Aber dann habe ich mich engagiert, mich stärker verliebt. Nun, im Sinne von etwas strenger. Ich habe die Option strictNullChecks im Projekt aktiviert und drei Tage damit verbracht, die aufgetretenen Fehler zu beheben. Und dann freute er sich zufrieden und stellte fest, wie einfach und uneingeschränkt das Refactoring jetzt ist.





Aber dann willst du noch mehr. Und hier muss das Typoskript erklären, welche Einschränkungen Sie sich selbst auferlegen, und Sie delegieren ihm die Verantwortung, die Einhaltung dieser Einschränkungen zu überwachen. Komm schon, mach mich fertig.





Beispiel 1

Vor einiger Zeit war ich von der Idee fasziniert, React als Template-Engine auf dem Server zu verwenden. Erfasst natürlich durch die Möglichkeit des Tippens. Ja, es gibt alle Arten von Mops, Schnurrbart und was sonst noch da. Der Entwickler muss jedoch selbst bedenken, ob er vergessen hat, das an die Vorlage übergebene Argument um neue Felder zu erweitern. (Wenn dies nicht so ist, korrigieren Sie mich. Aber im Allgemeinen ist es mir egal - Gott sei Dank muss ich mich nicht mit der Generierung von Vorlagen aufgrund der Art meiner Arbeit befassen. Und ein Beispiel für etwas anderes).





Und hier können wir normalerweise die an die Komponente übergebenen Requisiten eingeben und beim Bearbeiten der Vorlage die entsprechenden IDE-Hinweise erhalten. Dies ist jedoch innerhalb der Komponente. Stellen wir nun sicher, dass wir keinen Linkismus auf diese Komponente übertragen haben.





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



Wenn wir nun versuchen, Requisiten aus der Bestellung auf die Benutzerkomponente zu übertragen, werden wir sofort auf dieses Missverständnis aufmerksam gemacht. Cool? Cool.





Dies ist jedoch zum Zeitpunkt der HTML-Generierung. Wie läuft es mit seiner weiteren Verwendung? weil Das Ergebnis der Instanziierung von Rendered ist nur eine Zeichenfolge. Typoskript schwört beispielsweise nicht auf die folgende Konstruktion:





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



Wenn wir also so etwas schreiben:





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



Dies garantiert in keiner Weise, dass das Kompilierungsergebnis der HelloWorldComponent von dieser Methode zurückgegeben wird .





, :)





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. .





Hier können Sie auch feststellen, dass die Kommunikation zwischen den Komponenten des Systems über Schnittstellen entkoppelt werden muss. Es ist jedoch durchaus möglich, dass die empfangende Seite vorschreibt, dass sie eine IApiResponse erwartet , aber der Dienst von der Domänenlogikschicht ist so, sei es, lassen Sie sie eine bestimmte Implementierung von ApiResponse zurückgeben .





Gut ...

Ich hoffe dieses Material war interessant für dich. Für einige mag dieser Ansatz überflüssig erscheinen, und ich fordere nicht alle auf, dringend solche "Wachen" zu ihren Projekten hinzuzufügen. Aber ich hoffe, Sie haben in meinem Artikel Denkanstöße gefunden.





Vielen Dank für Ihre Zeit. Ich würde mich über konstruktive Kritik freuen.








All Articles