É isso que adoro no texto datilografado, porque me impede de espancar bobagens. Meça o comprimento de um valor numérico, etc. A princípio, é claro, cuspi, indignado por estar sendo incomodado com todo tipo de formalidades estúpidas. Mas então eu me envolvi, me apaixonei ainda mais. Bem, no sentido de um pouco mais estrito. Ativei a opção strictNullChecks no projeto e passei três dias corrigindo os erros que surgiram. E então ele se alegrou com satisfação, observando como a refatoração é fácil e irrestrita agora.
Mas então você quer algo ainda mais. E aqui o texto datilografado precisa explicar quais restrições você impõe a si mesmo, e você delega a ele a responsabilidade de monitorar o cumprimento dessas restrições. Vamos, quebre-me completamente.
Exemplo 1
Algum tempo atrás, fui capturado pela ideia de usar o react como um mecanismo de templates no servidor. Capturado, é claro, pela possibilidade de digitação. Sim, tem todos os tipos de pug, bigode e tudo o mais. Mas o desenvolvedor deve ter em mente se ele se esqueceu de expandir o argumento passado para o modelo com novos campos. (Se não for assim, corrija-me. Mas, em geral, eu não me importo - graças a Deus eu não tenho que lidar com a geração de modelos pela natureza do meu trabalho. E um exemplo sobre outra coisa).
E aqui podemos digitar normalmente os adereços passados para o componente e obter as dicas IDE apropriadas ao editar o modelo. Mas isso está dentro do componente. Agora, vamos ter certeza de que não transferimos nenhum esquerdismo para 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),
));
}
}
Agora, se tentarmos transferir adereços do pedido para o componente do usuário, seremos imediatamente alertados desse mal-entendido. Frio? Frio.
Mas isso é na hora da geração do html. Como vão as coisas com seu uso posterior? Porque o resultado de instanciar Rendered é apenas uma string, então o texto digitado não funcionará, por exemplo, com a seguinte construção:
const html: Rendered<SomeProps> = 'Typescript cannot into space';
Conseqüentemente, se escrevermos algo assim:
@Get()
public index(): Rendered<IHelloWorld> {
return new Rendered(HelloWorldComponent, helloWorldProps);
}
isso não garante de forma alguma que o resultado da compilação do HelloWorldComponent será retornado desse 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. .
Aqui você também pode notar que a comunicação entre os componentes do sistema deve ser desacoplada por meio de interfaces. Mas é bem possível que o lado receptor prescreva que espera um IApiResponse , mas o serviço da camada lógica do domínio é assim, deixe-o retornar uma implementação específica de ApiResponse .
Nós vamos ...
Espero que este material seja interessante para você. Para alguns, essa abordagem pode parecer redundante, e não incentivo a todos que adicionem urgentemente esses "guardas" a seus projetos. Mas espero que você tenha encontrado algo para refletir em meu artigo.
Obrigado pelo seu tempo. Eu ficaria feliz em receber críticas construtivas.