Trucos simples de TypeScript para escalar sus aplicaciones infinitamente

Usamos TypeScript porque hace que el desarrollo sea más seguro y rápido.

Pero en mi opinión, TypeScript contiene demasiadas indulgencias listas para usar. Ayudan a ahorrar un poco de tiempo para los desarrolladores de JavaScript cuando se cambian a TS, pero consumen mucho tiempo a largo plazo.

He reunido un conjunto de configuraciones y pautas para un uso más riguroso de TypeScript. Necesita acostumbrarse a ellos una vez, y le ahorrarán mucho tiempo en el futuro.

alguna

La regla más simple que le da a mi equipo muchas ganancias a largo plazo: 

No use ninguno.

Prácticamente no hay situaciones en las que no pueda describir un tipo en lugar de utilizar ninguno. Si me encuentro en una situación en la que tengo que escribir algo en un proyecto a largo plazo, generalmente encuentro problemas en la arquitectura o en su código heredado.

Utilice tipos genéricos , desconocidos o sobrecargas y podrá olvidarse de errores inesperados al trabajar con datos. A veces, estos problemas son difíciles de detectar y bastante costosos de 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