Uso de propiedades de clases privadas para fortalecer la escritura mecanografiada

Esto es lo que me encanta de la escritura mecanografiada porque evita que yo diga tonterías. Mide la longitud de un valor numérico, etc. Al principio, por supuesto, escupí, indignado porque me estaban molestando con todo tipo de formalidades estúpidas. Pero luego me involucré, me enamoré más. Bueno, en el sentido de un poco más estricto. Activé la opción strictNullChecks en el proyecto y pasé tres días corrigiendo los errores que surgieron. Y luego se regocijó con satisfacción, notando lo fácil y sin restricciones que es ahora la refactorización.





Pero luego quieres algo aún más. Y aquí el texto mecanografiado debe explicar qué restricciones se impone a sí mismo, y delega en él la responsabilidad de supervisar el cumplimiento de estas restricciones. Vamos, rómpeme por completo.





Ejemplo 1

Hace algún tiempo, me cautivó la idea de usar react como motor de plantillas en el servidor. Capturado, por supuesto, por la posibilidad de escribir. Sí, hay todo tipo de pug, bigote y qué más. Pero el desarrollador debe tener en cuenta si se olvidó de expandir el argumento pasado a la plantilla con nuevos campos. (Si no es así, corrígeme. Pero en general, no me importa - gracias a Dios no tengo que ocuparme de la generación de plantillas por la naturaleza de mi trabajo. Y un ejemplo sobre otra cosa).





Y aquí normalmente podemos escribir los accesorios pasados ​​al componente y obtener las sugerencias IDE adecuadas al editar la plantilla. Pero esto está dentro del componente. Ahora asegurémonos de que no hayamos trasladado ningún izquierdismo a este componente.





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



Ahora, si intentamos transferir accesorios del pedido al componente de usuario, seremos alertados de inmediato sobre este malentendido. ¿Fresco? Fresco.





Pero esto es en el momento de la generación html. ¿Cómo van las cosas con su uso posterior? Porque el resultado de instanciar Rendered es solo una cadena, entonces el mecanografiado no jurará, por ejemplo, con la siguiente construcción:





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



En consecuencia, si escribimos algo como esto:





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



esto no garantiza de ninguna manera que el resultado de la compilación de HelloWorldComponent sea ​​devuelto por este método .





, :)





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





Aquí también puede observar que la comunicación entre los componentes del sistema debe desacoplarse a través de interfaces. Pero es muy posible que el lado receptor prescriba que espera una IApiResponse , pero el servicio de la capa lógica de dominio es así, que devuelva una implementación específica de ApiResponse .





Bien ...

Espero que este material te haya resultado interesante. Para algunos, este enfoque puede parecer redundante, y no insto a todos a que agreguen urgentemente tales "guardias" a sus proyectos. Pero espero que hayas encontrado algo en que pensar en mi artículo.





Gracias por tu tiempo. Me complacería recibir críticas constructivas.








All Articles