let name: type[= initial]
--------------------------
- name: Variable name
- type: Variable type
- initial: Initial value
Example 1:
let age: number = 20;
console.log(age);
Since TypeScript checks types, you cannot store a string in a number type variable.
let age: number = 20;
age = 'Twenty';
// Error
// boolean type
let flag: boolean = false;
// number type
let age: number = 30; // Decimal
let binary: number = 0b11; // Binary
let exp: number = 1.23e-5; // Exponential notation
// string type
let name: string = 'John Doe';
// string type (template string)
let meibo = `Name: ${name}`;
console.log(meibo); => John Doe
// Array type
let os: string[] = ['macOS', 'Windows', 'Linux'];
console.log(os[2]); => Linux
// Object type (associative array)
let duet: { [index: string]: string } =
{ first: 'John', second: 'Smith'};
console.log(duet['first']);
In TypeScript, the ability to specify the index/value type of an associative array is called an index signature.
{ [index: string]: string }
index type value type
Although it's called an index signature, "index" is just a placeholder name, so you could use "key" instead.
The index signature notation can also be extracted as an interface instruction that defines only the signatures of methods (name, arguments, return value type) required to implement a specific functionality, without including the actual processing.
interface MyMap {
[index: string]: string;
}
let duet: MyMap = { first: 'John', second: 'Smith' };
// Tuple type
A tuple is a data structure that can hold multiple values like a list.
- Immutable (cannot be changed)
- Slightly faster execution speed
let score: [number, string] = [100, 'great'];
console.log(score[0]); => 100
console.log(score[1]); => great
// enum type (enumeration)
The enum type (enumeration) is used when it is convenient to assign a series of integer values to multiple variables.
enum Color { Red, Green, Blue };
let red: Color = Color.Red;
console.log(red); => 0
console.log(Color[red]); => Red
const name: type = value
--------------------------
- name: Constant name
- type: Constant type
- value: Constant value
Example 1:
const PI: number = 3.14;
PI = 3.141 // Error when trying to reassign
Type casting
. Type conversion.
<type> variable
--------------------------
- type: Type to convert to
- variable: Variable to be type-converted
Example 1:
function greet(name: string) {
return 'Hello, ' + name;
}
// Passing a number to the string type parameter name (cast to any type)
console.log(greet(<any>108));
// Can also be replaced with as syntax
// as syntax is also ok instead of <...>
console.log(greet(108 as any));
function name(arg : type, ...) : r_type {
...body...
}
--------------------------
- name: Function name
- arg: Parameter
- type: Parameter type
- r_type: Return value type
- body: Function body
Example 1:
// No parameters or return value. void type means a type that has no value.
function greet(): void {
console.log('Hello');
}
greet(); => Hello
// With parameters and return value
function add(x: number, y: number): number {
return x + y;
}
console.log(add(4, 5));
// To make a parameter optional, append ? to the parameter name.
function showCost(price: number, discount?: number) {
if (discount) {
console.log(`Cost: ${price - discount}`);
} else {
console.log(`Cost: ${price}`);
}
}
showCost(1000);
showCost(1000, 200)
// Forgetting to append ? to the parameter will result in an error
function showCost(price: number, discount: number) {
if (discount) {
console.log(`Cost: ${price - discount}`);
} else {
console.log(`Cost: ${price}`);
}
}
showCost(1000); => Expected 2 arguments, but got 1.
showCost(1000, 200)
// Parameter default values
When specifying default values, parameters become optional unconditionally, so ? is unnecessary. Specifying it will cause an error.
function showCost(price: number, discount: number = 0) {
if (discount) {
console.log(`Cost: ${price - discount}`);
} else {
console.log(`Cost: ${price}`);
}
}
showCost(1000);
showCost(1000, 200)
// Variable length arguments "..."
function sum(...values: number[]): number {
let result: number = 0;
// The variable length argument values can be processed as an array
for (let i: number = 0; i < values.length; i++) {
result += values[i];
}
return result;
}
console.log(sum(100, 200, 300));
(arg : type, ...) : r_type => {
...body...
}
--------------------------
- arg: Parameter
- type: Parameter type
- r_type: Return value type
- body: Function body
Example 1:
// Rewriting the following with an arrow function
function add(x: number, y: number): number {
return x + y;
}
let add = (x: number, y: number): number => {
return x + y;
};
If there's only one argument and no type declarations for arguments and return values, you can omit the parentheses around the argument, but there's not much need to do so.
Arrow functions have the property that this is fixed when declared, so it's preferable to use arrow functions over function commands whenever possible.
function name(arg : type, ...) : r_type;
function name(arg : type, ...) : r_type;
...
function name(arg : type, ...) : r_type; {
...body...
}
--------------------------
- name: Function name
- arg: Parameter
- type: Parameter type
- r_type: Return value type
- body: Function body
Function overloading
means defining multiple functions with the same name but different parameter types or orders. However, the syntax for TypeScript overloading is to first list functions with only signatures (no implementation), and then write a “catch-all” function with a body that can handle all overloads.
It’s like writing type determination in a case statement.
function search(str: string, start: number): string;
function search(str: string, start: string): string;
function search(str: string, start: any): string {
if (typeof start === 'number') {
return str.substring(start);
} else {
return str.substring(str.indexOf(start));
}
}
let msg = 'abcdefghijklmn';
console.log(search(msg, 3));
console.log(search(msg, 'f'));
// Variable variable is either type1 or type2
variable: type1 | type2
--------------------------
- variable: Variable
- type1, 2: Types
A union type
is a type that binds multiple types to a variable.
Example 1:
function search(str: string, start: number): string;
function search(str: string, start: string): string;
function search(str: string, start: any): string {
if (typeof start === 'number') {
return str.substring(start);
} else {
return str.substring(str.indexOf(start));
}
}
let msg = 'abcdefghijklmn';
console.log(search(msg, 3));
console.log(search(msg, 'f'));
Rewriting the above using union types:
function search(str: string, start: number | string): string {
if (typeof start === 'number') {
return str.substring(start);
} else {
return str.substring(str.indexOf(start));
}
}
let msg = 'abcdefghijklmn';
console.log(search(msg, 3));
console.log(search(msg, 'f'));
Rewriting further using arrow function:
let search = (str: string, start: number | string): string => {
if (typeof start === 'number') {
return str.substring(start);
} else {
return str.substring(str.indexOf(start));
}
}
let msg = 'abcdefghijklmn';
console.log(search(msg, 3));
console.log(search(msg, 'f'));
class name {
modifier variable: p_type...
modifier constructor(modifier arg: a_type,...) { ...c_body... }
modifier method(arg: a_type,...): r_type { ...m_body... }
}
--------------------------
- name: Class name
- modifier: Modifier
- variable: Property name
- p_type: Property type
- arg: Argument
- a_type: Argument type
- c_body: Constructor body
- method: Method name
- r_type: Return value type
- m_body: Method body
Example 1:
class Dog {
// Constructor
constructor(private category: string, private weight: number) { }
// toString method
public toString(): string {
return this.category + ':' + this.weight + 'kg'
}
}
let mame = new Dog('Shiba', 8);
console.log(mame.toString());
Static members are fields or methods that belong to the class rather than to a specific instance.
class FigureUtil {
// Static property PI declaration
static PI = 3.14;
// Static method circle declaration
static circle(radius: number): number {
return radius * radius * this.PI;
}
}
console.log(FigureUtil.PI);
console.log(FigureUtil.circle(2));
get name(): type { ... }
set name(value: type) { ... }
--------------------------
- name: Property name
- type: Property type
- value: Value passed when setting
get/set accessors can be accessed from outside the class as if they were properties. → They are mechanisms for implementing behavior when getting/setting values internally as methods, rather than simple variables.
By using get/set accessors, you can implement “arbitrary processing associated with the input/output of values” that cannot be expressed by properties (member variables) alone, such as validating the value when setting it or converting the value when retrieving it.
class Dog {
// Private variable to store the weight property value
private _weight: number;
// Get weight property
get weight(): number {
return this._weight;
}
// Set weight property (exception if less than 0)
set weight(value: number) {
if (value < 0) {
throw new Error('The weight property must be specified as a positive number.');
}
this._weight = value;
}
}
let poodle = new Dog();
poodle.weight = -13; // => The weight property must be specified as a positive number.
class name extends parent {
...body...
}
--------------------------
- name: Class name
- parent: Base class name
- body: Class definition
extends keyword → Inheriting from an existing class
super keyword → Calling base class (superclass) methods from a derived class (subclass)
class Dog {
constructor(protected category: string, protected weight: number) { }
toString(): string {
return this.category + ':' + this.weight + 'Kg'
}
}
// UltraDog class inheriting from Dog class
class UltraDog extends Dog {
toString(): string {
// Call the toString method of the base class
return 'Super' + super.toString();
}
}
let dober = new UltraDog('Doberman', 35);
console.log(dober.toString()); => SuperDoberman:35Kg
To call a parent method, use super.methodName(...)
.
It’s also possible to override the constructor and call the parent constructor from within it. In that case, simply use super(...)
.
interface name extends s_name {
...body...
}
--------------------------
- name: Interface name
- s_name: Parent interface name
- body: Interface body
An interface
is like a class with only signatures and no implementation, used to enforce certain functionalities (implementations) on classes.
An interface
is like a contract. Unlike class inheritance, implementing an interface
creates an entry point to reference other functionality.
Example:
Electricity Consumers → Power Company ← Thermal Power, Nuclear Power, or Wind Power Generation
Consumers can use electricity (methods) formally without being aware of how the electricity is produced (implementation).
To define an interface
itself, use the interface directive. Like classes, you can inherit from other interfaces using the extends keyword.
// Pet interface
interface Pet {
name: string; // Property definition
call(): void; // Method definition
}
// Declare a class implementing Pet
class Dog implements Pet {
name: string;
call(): void {
console.log(this.name + '-san');
}
}
let pochi = new Dog();
pochi.name = 'Pochi';
pochi.call(); => Pochi-san
// Power Contract interface
interface PowerContract {
method: string;
call(): void;
}
// Declare a Power Company class implementing PowerContract
class PowerCompany implements PowerContract {
method: string;
call(): void {
console.log('Supplying electricity using ' + this.method);
}
}
let tepco = new PowerCompany();
tepco.method = 'nuclear power';
tepco.call(); => Supplying electricity using nuclear power
To implement an interface, use the implements keyword.
To implement multiple interfaces, separate them with commas:
implements Pet, Movable
Using interfaces as type annotations.
interface Member {
name: string; // Property signature
greet(): void; // Method signature
}
// Declare a variable mem of Member type
let mem: Member = {
name: 'John Doe',
greet() {
console.log('Hello, ' + this.name);
}
};
mem.greet(): => Hello, John Doe
// AddFunc type that takes number type arguments a, b and returns a number type value
interface AddFunc {
(a: number, b: number): number
}
// Define a function of AddFunc type
let add: AddFunc = function (x: number, y: number): number {
return x + y;
};
{ [index: string]: string }
index type value type
class name<T> {
...body...
}
--------------------------
- name: Class name
- T: Type parameter
- body: Class body
Generics
are a feature for associating general-purpose classes and methods with specific types.
class GenericClass<T> {
data: T;
getData(): T {
return this.data;
}
}
let gen = new GenericClass<number>();
gen.data = 108;
console.log(gen.getData()); => 108
To define a generic type, simply append a type parameter like
When instantiating a generic type, append the type to be bound in the form <…>. In the example, the type parameter T is assigned the number type. A GenericClass object bound to the number type is generated.
This is written in a complicated way, but basically, it’s saying that when you specify a type when creating a GenericClass object, T is conveniently bound to that type.
Reference: Angular Application Programming
Although this is an Angular book, it has a good introduction to TypeScript as well.