Truques TypeScript simples para dimensionar seus aplicativos infinitamente

Usamos TypeScript porque torna o desenvolvimento mais seguro e rápido.

Mas, em minha opinião, o TypeScript contém muitas indulgências fora da caixa. Eles ajudam a economizar um pouco de tempo para os desenvolvedores de JavaScript na mudança para o TS, mas consomem muito tempo no longo prazo.

Eu reuni um conjunto de configurações e diretrizes para um uso mais rigoroso do TypeScript. Você precisa se acostumar com eles uma vez - e eles economizarão muito tempo no futuro.

qualquer

A regra mais simples que dá à minha equipe muito lucro a longo prazo: 

Não use nenhum.

Praticamente não há situações em que você não possa descrever um tipo em vez de usar nenhum. Se eu me encontrar em uma situação em que tenho que escrever algum em um projeto de longo prazo, geralmente encontro problemas na arquitetura ou no código legado.

Use tipos genéricos , desconhecidos ou sobrecargas e você pode esquecer os erros de dados inesperados. Esses problemas às vezes são difíceis de detectar e muito caros para depurar.

, any, — № 3.

strict

TypeScript strict-, , , . TypeScript. , .

strict- undefined is not a function cannot read property X of null. .

-?

, strict .

strict- , . , . , , , .

, — . strict- . . . , . strict-!

, . , @ts-ignore TODO. .

// tsconfig.json file
{
    // ...,
    "compilerOptions": {
        // a set of cool rules
        "noImplicitAny": true,
        "noImplicitThis": true,
        "strictNullChecks": true,
        "strictPropertyInitialization": true,
        "strictBindCallApply": true,
        "strictFunctionTypes": true,
        // a shortcut enabling 6 rules above
        "strict": true,
        // ...
    }
}

readonly

— readonly.

, , — . , Angular- : , .

? readonly .

?

, , , readonly-.

readonly :

// before
export interface Thing {
    data: string;
}

// after
export interface Thing {
    readonly data: string;
}

readonly :

// Before
export type UnsafeType = { prop: number };

// After
export type SafeType = Readonly<{ prop: number }>;

readonly , :

// Before
class UnsafeComponent {
    loaderShow$ = new BehaviorSubject<boolean>(true);
}

// After
class SafeComponent {
    readonly loaderShow$ = new BehaviorSubject<boolean>(true);
}

readonly-:

// Before
const unsafeArray: Array<number> = [1, 2, 3];
const unsafeArrayOtherWay: number[] = [1, 2, 3];

// After
const safeArray: ReadonlyArray<number> = [1, 2, 3];
const safeArrayOtherWay: readonly number[] = [1, 2, 3];

// three levels
const unsafeArray: number[] = [1, 2, 3]; // bad
const safeArray: readonly number[] = [1, 2, 3]; // good
const verySafeTuple: [number, number, number] = [1, 2, 3]; // super
const verySafeTuple: readonly [number, number, number] = [1, 2, 3]; // awesome (after v3.4)

// Map:
// Before
const unsafeMap: Map<string, number> = new Map<string, number>();

// After
const safeMap: ReadonlyMap<string, number> = new Map<string, number>();


// Set:
// Before
const unsafeSet: Set<number> = new Set<number>();

// After
const safeSet: ReadonlySet<number> = new Set<number>();

as const

TypeScript v3.4 const-assertions. , readonly-, . : .

, as const IDE .

Utility Types

TypeScript , .

Utility Types . .

TypeScript . , .

. , :

( repl.it)

import {Subject} from 'rxjs';
import {filter} from 'rxjs/operators';

interface Data {
  readonly greeting: string;
}

const data$$ = new Subject<Data | null>();

/**
 * source$     "Observable<Data | null>"
 *  "null"     filter
 * 
 *  ,   TS    ,    .
 *  "value => !!value"  boolean,      
 */
const source$ = data$$.pipe(
  filter(value => !!value)
)

/** 
 *      
 * 
 *      ,   value  Data.
 *   ,   "wellTypedSource$"  
 */
const wellTypedSource$ = data$$.pipe(
  filter((value): value is Data => !!value)
)

//   ,   :)
// source$.subscribe(x => console.log(x.greeting));

wellTypedSource$.subscribe(x => console.log(x.greeting));

data$$.next({ greeting: 'Hi!' });

:

  • typeof — JavaScript .

  • instanceof — JavaScript .

  • is T — TypeScript, . , TS’ .

:

( repl.it)

// typeof narrowing
function getCheckboxState(value: boolean | null): string {
   if (typeof value === 'boolean') {
       // value has "boolean" only type
       return value ? 'checked' : 'not checked';
   }

   /**
    *     value   “null”
    */
   return 'indeterminate';
}

// instanceof narrowing
abstract class AbstractButton {
   click(): void { }
}

class Button extends AbstractButton {
   click(): void { }
}

class IconButton extends AbstractButton {
   icon = 'src/icon';

   click(): void { }
}

function getButtonIcon(button: AbstractButton): string | null {
   /**
    *  "instanceof" TS ,       "icon"
    */
   return button instanceof IconButton ? button.icon : null;
}

// is T narrowing
interface User {
   readonly id: string;
   readonly name: string;
}

function isUser(candidate: unknown): candidate is User {
   return (
       typeof candidate === "object" &&
       typeof candidate.id === "string" &&
       typeof candidate.name === "string"
   );
}

const someData = { id: '42', name: 'John' };

if (isUser(someData)) {
   /**
    *  TS ,  someData   User
    */
   console.log(someData.id, someData.name)
}

, , , . , TypeScript, , , , . .




All Articles