Quick Start

Kesa...大约 7 分钟

Compiler

TypeScript is transpiled into JavaScript using a compiler.

Installation

# install compiler
$ npm install typescript --save-dev
# run compiler
$ npx src
# configure the compiler
$ npx tsc --init

Type Assignment

There are two main TypeScript assigns a type:

  • Explicit
  • Implicit
let name: string = "Alice";
let name2 = "Alice";

any

any is a type that disables type checking and effectively allows all types to be used.

let v: any = true;
v = "string";
v = 12;

unknown

unknown is a similar, but safer alternative to any.

TypeScript will prevent unknown types from being used:

let w: unknown = 1;
console.log(w);

w = "Alice";
console.log(w);

console.log(typeof w);
console.log(w.slice(1,2)); // error 

never

never effectively throws an error whenever it is defined.

let x: never = true; // Error: Type 'boolean' is not assignable to type 'never'.

never is rarely used, especially by itself, its primary use is in advanced generics.

undefined and null

undefined and null are types that refer to the JavaScript primitives undefined and null respectively.

let y: undefined = undefined;
ley z: null = null;

These types don't have much use unless strictNullChecks is enabled in the tsconfig.json file.

Array

const names: string[] = [];
names.push("Alice");
names.push(1); // error

Readonly

The readonly keyword can prevent arrays from being changed.

const names: readonly string[] = ["Alice"];
names.push("A"); // error

Type inference

TypeScript can infer the type of an array if it has values.

const nums = [1,2,3];
nums.push(4); 
nums.push("A"); // error

Tuple

tuple is a typed array with a pre-defined length and types for each index.

// define tuple
let t: [number, boolean, string];
// init
t = [5, false, "Alice"];

If you have ever used React before you have worked with tuples more than likely.

useState returns a tuple of the value and a setter function.

const [firstName, setFirstName] = useState('Dylan') is a common example.

Because of the structure we know our first value in our list will be a certain value type in this case a string and the second value a function.

Named tuple

const graph: [x: number, y: number] = [1.0, 2.0];

Destructuring Tuples

const graph: [number, number] = [55.2, 41.3];
const [x, y] = graph;

Object

const car: {
    type: string,
    model: string,
    year: number
} = {
  type: "Toyota",
  model: "Corolla",
  year: 2009
};

Type inference

const car = {
    type: "Toyota",
};
car.type = "Ford";
car.type = 2; // error

Optional properties

//  Error: Property 'mileage' is missing in type '{ type: string; }' but required in type '{ type: string; mileage: number; }'.
const car: {
    type: string,
    mileage: number
} = {
    type: "Toyota",
};
car.mileage = 2000;
const car: {
    type: string,
    mileage?: number
} = {
    type: "Toyota",
};
car.mileage = 2000;

Index signature

Index signature can be used for objects without a defined list of properties.

const nameAgeMap: {[index: string]: number} = {};
nameAgeMap.Jack = 25;
nameAgeMap.Mark = "Fifty"; // error

Enum

An enum is a special “class” that represents a group of constants.

Enums come in two flavors string and numeric.

Numeric enums (default)

By default, enums will initialize the first value to 0 and add 1 to each additional value:

enum CardinalDirections {
    North,
    East,
    South,
    West
}

let currentDir = CardianlDirections.North;
console.log(currentDir); // 0
currentDir = "North"; // error

You can set the value of the first numeric enum and have it auto increment from that:

enum Dirs {
    North = 1,
    East,
    South,
    West
}

console.log(Dirs.North); // 1
console.log(Dirs.East); // 2

You can assign unique number values for each enum value.

enum Dirs {
    North = 100,
    East = 200,
    South = 300,
    West = 400
}

String enums

enum CardinalDirections {
  North = 'North',
  East = "East",
  South = "South",
  West = "West"
};
// logs "North"
console.log(CardinalDirections.North);
// logs "West"
console.log(CardinalDirections.West);

Type alias and interface

Type alias

// Try creating a new Car using the alias provided
type CarYear = number;
type CarType = string;
type CarModel = string;
type Car = {
  year: CarYear,
  type: CarType,
  model: CarModel
};

const carYear: CarYear = 2001
const carType: CarType = "Toyota"
const carModel: CarModel = "Corolla"
const car: Car = {
  year: carYear,
  type: carType,
  model: carModel
};

console.log(car);

Interface

Interfaces are similar to type aliases, except they only apply to object types.

// Try creating a new interface using it below
interface Rectangle {
  height: number,
  width: number
};

const rectangle: Rectangle = {
  height: 20,
  width: 10
};

console.log(rectangle);

Extending interfaces

Extending an interface means you are creating a new interface with the same properties as the original, plus something new.

// Try creating a new interface and extending it like below
interface Rectangle {
  height: number,
  width: number
}

interface ColoredRectangle extends Rectangle {
  color: string
}

const coloredRectangle: ColoredRectangle = {
  height: 20,
  width: 10,
  color: "red"
};

console.log(coloredRectangle);

Union Type

Union types are used when a value can be more than a single type.

function printStatusCode(code: string | number) {
    console.log(`Code is ${code}.`);
}

printStatusCode(404);
printStatusCode("404");

Function

The type of the value returned by the function can be explicitly defined.

function getTime(): number {
    return new Date().getTime();
}

// void return type
func printHello(): void {
    console.log("hello");
}

Parameter type

function multiply(a: number, b: number) {
    return a*b;
}

If no parameter type is defined, TypeScript will default to using any, unless additional type information is available as shown in the Default Parameters and Type Alias sections below.

Optional parameter

function add(a: number, b: number, c?: number) {
    return a + b + (c || 0);
}

Default parameter

function pow(val: number, exponent: number = 10) {
    return val ** exponent;
}

Named parameter

function divide({dividend, divisor}: {dividend: number, divisor: number}) {
    return dividend / divisor;
}

Rest parameters

Rest parameters can be typed like normal parameters, but the type must be an array as rest parameters are always arrays.

function add(a: number, b: number, ...rest: number[]) {
    return a + b + rest.reduce((p, c) => p + c, 0);
}

Type alias

type Negate = (value: number) => number;

const negateFuntion: Negate = (value) => value * -1;

Type casting

as

as will directly change the type of the given variable.

let x: unknown = "hello";
console.log((x as string).length);

Casting doesn’t change the type of the data within the variable.

let x: unknown = 4;
console.log((x as string).length); // undefined

TypeScript will still attempt to typecheck casts to prevent casts that don't seem correct.

// Error: Conversion of type 'number' to type 'string' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
console.log((4 as string).length); 

<>

Using <> works the same as casting with as.

let x: unknown = "Hello";
console.log((<string>x).length);

This type of casting will not work with TSX, such as when working on React files.

Class

TypeScript adds types and visibility modifiers to JavaScript classes.

class Person {
    name: string;
}
const p = new Person();
p.name = "Alice";

Visibility

  • public - default, allows access to the class member from anywhere
  • private - only allows access to the class member from within the class
  • protected - allows access to the class member from itself and any classes that inherit it, which is covered in the inheritance below
class Person {
  private name: string;

  public constructor(name: string) {
    this.name = name;
  }

  public getName(): string {
    return this.name;
  }
}
      
const person = new Person("Jane");

console.log(person.getName()); // person.name isn't accessible from outside the class since it's private

Readonly

readonly keyword can prevent class members from being changed.

class Person {
  private readonly name: string;

  public constructor(name: string) {
    // name cannot be changed after this initial definition, which has to be either at it's declaration or in the constructor.
    this.name = name;
  }

  public getName(): string {
    return this.name;
  }
}
      
const person = new Person("Jane");

console.log(person.getName());

Inheritance

Classes can extend each other through the extends keyword. A class can only extends one other class.

interface Shape {
  getArea: () => number;
}
      
class Rectangle implements Shape {
  public constructor(protected readonly width: number, protected readonly height: number) {}

  public getArea(): number {
    return this.width * this.height;
  }
}
      
class Square extends Rectangle {
  public constructor(width: number) {
    super(width, width);
  }
  // getArea gets inherited from Rectangle
}

const mySq = new Square(20);

console.log(mySq.getArea());

Override

When a class extends another class, it can replace the members of the parent class with the same name.

Newer versions of TypeScript allow explicitly marking this with the override keyword.

interface Shape {
  getArea: () => number;
}

class Rectangle implements Shape {
  // using protected for these members allows access from classes that extend from this class, such as Square
  public constructor(protected readonly width: number, protected readonly height: number) {}

  public getArea(): number {
    return this.width * this.height;
  }

  public toString(): string {
    return `Rectangle[width=${this.width}, height=${this.height}]`;
  }
}

class Square extends Rectangle {
  public constructor(width: number) {
    super(width, width);
  }

  // this toString replaces the toString from Rectangle
  public override toString(): string {
    return `Square[width=${this.width}]`;
  }
}

const mySq = new Square(20);

console.log(mySq.toString());

By default the override keyword is optional when overriding a method, and only helps to prevent accidentally overriding a method that does not exist. Use the setting noImplicitOverride to force it to be used when overriding.

Abstract Classes

Classes can be written in a way that allows them to be used as a base class for other classes without having to implement all the members. This is done by using the abstract keyword. Members that are left unimplemented also use the abstract keyword.

abstract class Polygon {
  public abstract getArea(): number;

  public toString(): string {
    return `Polygon[area=${this.getArea()}]`;
  }
}

class Rectangle extends Polygon {
  public constructor(protected readonly width: number, protected readonly height: number) {
    super();
  }

  public getArea(): number {
    return this.width * this.height;
  }
}

const myRect = new Rectangle(10,20);

console.log(myRect.getArea());

Generic

Function

function createPair<S, T>(v1: S, v2: T): [S, T] {
    return [v1, v2];
}
console.log(createPair<string, number>("hello", 42)); // ["hello", 42]

Class

class NamedValue<T> {
  private _value: T | undefined;

  constructor(private name: string) {}

  public setValue(value: T) {
    this._value = value;
  }

  public getValue(): T | undefined {
    return this._value;
  }

  public toString(): string {
    return `${this.name}: ${this._value}`;
  }
}
      
const value = new NamedValue<number>('myNumber');

value.setValue(10);

console.log(value.toString()); // myNumber: 10

Type alias

type Wrapped<T> = { value: T };

const wrappedValue: Wrapped<number> = { value: 10 };

Extend

function createLoggedPair<S extends string | number, T extends string | number>(v1: S, v2: T): [S, T] {
  console.log(`creating pair: v1='${v1}', v2='${v2}'`);
  return [v1, v2];
}

Utility Type

Partial

Partial changes all the properties in an object to be optional.

interface Point {
  x: number;
  y: number;
}

let pointPart: Partial<Point> = {}; // `Partial` allows x and y to be optional
pointPart.x = 10;

Required

Required changes all the properties in an object to be required.

interface Car {
  make: string;
  model: string;
  mileage?: number;
}

let myCar: Required<Car> = {
  make: 'Ford',
  model: 'Focus',
  mileage: 12000 // `Required` forces mileage to be defined
};

Record

Record is a shortcut to defining an object type with a specific key type and value type.

const nameAgeMap: Record<string, number> = {
  'Alice': 21,
  'Bob': 25
};

Omit

Omit removes keys from an object type.

interface Person {
  name: string;
  age: number;
  location?: string;
}

const bob: Omit<Person, 'age' | 'location'> = {
  name: 'Bob'
  // `Omit` has removed age and location from the type and they can't be defined here
};

Pick

Pick removes all but the specified keys from an object type.

interface Person {
  name: string;
  age: number;
  location?: string;
}

const bob: Pick<Person, 'name'> = {
  name: 'Bob'
  // `Pick` has only kept name, so age and location were removed from the type and they can't be defined here
};

Exclude

Exclude removes types from a union.

type Primitive = string | number | boolean
const value: Exclude<Primitive, string> = true; // a string cannot be used here since Exclude removed it from the type.

ReturnType

ReturnType extracts the return type of a function type.

type PointGenerator = () => { x: number; y: number; };
const point: ReturnType<PointGenerator> = {
  x: 10,
  y: 20
};

Parameters

Parameters extracts the parameter types of a function type as an array.

type PointPrinter = (p: { x: number; y: number; }) => void;
const point: Parameters<PointPrinter>[0] = {
  x: 10,
  y: 20
};

Readonly

Readonly is used to create a new type where all properties are readonly, meaning they cannot be modified once assigned a value.

Keep in mind TypeScript will prevent this at compile time, but in theory since it is compiled down to JavaScript you can still override a readonly property.

interface Person {
  name: string;
  age: number;
}
const person: Readonly = {
  name: "Dylan",
  age: 35,
};
person.name = 'Israel'; // prog.ts(11,8): error TS2540: Cannot assign to 'name' because it is a read-only property.

Reference

  1. https://w3schools.com/typescript/index.phpopen in new window
上次编辑于:
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.15.2