Kami menggunakan TypeScript karena membuat pengembangan lebih aman dan lebih cepat.
Tapi menurut saya, TypeScript mengandung terlalu banyak indulgensi di luar kotak. Mereka membantu menghemat sedikit waktu bagi pengembang JavaScript saat beralih ke TS, tetapi memakan banyak waktu dalam jangka panjang.
Saya telah mengumpulkan satu set pengaturan dan pedoman untuk penggunaan TypeScript yang lebih ketat. Anda harus membiasakan diri dengan mereka sekali - dan mereka akan menghemat banyak waktu di masa depan.
apa saja
Aturan paling sederhana yang memberi tim saya banyak keuntungan dalam jangka panjang:
Jangan gunakan apapun.
Praktis tidak ada situasi di mana Anda tidak dapat mendeskripsikan suatu tipe daripada menggunakannya. Jika saya menemukan diri saya dalam situasi di mana saya harus menulis apa pun dalam proyek jangka panjang, saya biasanya menemukan masalah dalam arsitektur atau kode warisannya.
Gunakan tipe generik , tidak diketahui atau kelebihan beban dan Anda bisa melupakan kesalahan tak terduga saat bekerja dengan data. Masalah seperti itu terkadang sulit ditangkap dan cukup mahal untuk di-debug.
, 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 , .
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, , , , . .