Type
Simple type aliasing
type CustomerName = string;
Useful when function have many parameters and it is easy to make mistakes when passing them. Just extract string
as domain specific type
const customerName: CustomerName = "Oleg";
Mixed types
type MaybeString = string | undefined;
type SomethingNobodyExpects = string | number | boolean;
Represent multiple possibilities when defining error
type Errors = string | string[] | undefined;
Type defined out of fixed literals
type FixedNumbers = 1 | 2 | 3 | 4;
type FixedStrings = "ONE" | "TWO" | "THREE";
Boolean looks strange, but still used
type IamAlwaysTrue = true;
type IamAlwaysFalse = false;
Complex object defined as new type
type SomethingBigger = {
a: number;
b: number;
};
Recursive types
Could include itself in definitions
type Tree = {
value: number;
left?: Tree;
right?: Tree;
};
const tree: Tree = {
value: 10,
left: {
value: 5,
right: {
value: 7,
},
},
};
console.log(tree);
Combining type definitions
Done with |
(OR) or &
(AND)
MyError
could be any of listed
class MyErrorClass {}
type MyError = Error | MyErrorClass;
And case &
used to merge types into one
type WithNumbers = {
one: number;
two: number;
};
type WithStrings = {
three: string;
four: string;
};
type CombinedObject = WithNumbers & WithStrings;
const combined: CombinedObject = {
one: 1,
two: 2,
three: "3",
four: "4",
};
console.log(combined.one);
console.log(combined.three);
All properties with same name will have resulting type never
. Do not do this !
Type vs Interface
Type generally is used for one liners, simple cases with |
and &
or functions
Interface is used for complex constructs
type GoodType = string | string[] | undefined;
type GoodFunctionType = (a: string, b: string) => string;
interface GoodDataInterface {
customerId: number;
age: number;
email: string;
}
Interface
Could have optional properties
interface WithOptionalProps {
definitelyHere: number;
goodDefinedOptional?: string; // prefer this way to define optional
notSoGood: string | undefined;
}
Represent partially undefined shape.
userId
should be there and everything else could present but not required to know how it is looks like
interface JsonDecodedData {
userId: string;
[anyNameHere: string]: any;
}
Usually keys are of typestring
. number
is also allowed but it is not convenient to use it.
Common use case is to parse json body and pass it to next service
const body = `{"userId": "1", "age":21, "name":"Bob"}`;
const apiRequest = JSON.parse(body) as JsonDecodedData;
if (apiRequest.userId !== undefined) {
console.log(apiRequest);
}
Extend interface
interface Base {
length: number;
}
interface Coordinate {
x: number;
y: number;
}
New interface extending others and adding additional properties
interface Field extends Base, Coordinate {
name: string;
}
const myField: Field = {
length: 100,
x: 10,
y: 20,
name: "My stuff",
};
New interface extending others but without any additional properties
interface NoNameField extends Base, Coordinate {}
const somebodyField: NoNameField = {
length: 100,
x: 10,
y: 20,
};
Could be also defined by type
type NoNameFieldType = Base & Coordinate;
Interface could force readonly properties
interface TryToAssign {
a?: number;
readonly b: string;
}
const readOnlyObject: TryToAssign = { b: "b" };
readOnlyObject.a = 10;
This will not work: readOnlyObject.b = "10"