简单的TypeScript技巧可无限扩展您的应用程序

我们使用TypeScript是因为它使开发更安全,更快捷。

但是在我看来,TypeScript包含了很多开箱即用的功能。当切换到TS时,它们为JavaScript开发人员节省了一些时间,但从长远来看却会浪费大量时间。

我整理了一组设置和指南,以更严格地使用TypeScript。您需要习惯一下它们-将来它们将节省大量时间。

任何

从长远来看,可以为我的团队带来很多利润的最简单的规则是: 

不要使用任何东西。

实际上,在任何情况下都无法描述类型而不使用任何类型。如果我发现自己不得不在一个长期项目中编写任何东西,通常会发现体系结构或其遗留代码存在问题。

使用通用类型未知重载,您可以忽略意外的数据错误。这些问题有时很难发现,调试起来也很昂贵。

, 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