21111

60 阅读7分钟

1. 入门

www.typescriptlang.org/docs/handbo…

(1) ts类型有哪些

There are already a small set of primitive types available in JavaScript: booleanbigintnullnumberstringsymbol, and undefined, which you can use in an interface. TypeScript extends this list with a few more, such as any (allow anything), unknown (ensure someone using this type declares what the type is), never (it’s not possible that this type could happen), and void (a function which returns undefined or has no return value).

调用unknown的属性和方法前必须先声明类型,比如:

type User = { name: string };
const myUserAccount = jsonParserUnknown(`{ "name": "Samuel" }`) as User;
myUserAccount.name;

(2) Interfaces and Types如何选择

You’ll see that there are two syntaxes for building types: Interfaces and Types. You should prefer interface. Use type when you need specific features.

(3) Composing Types

With TypeScript, you can create complex types by combining simple ones. There are two popular ways to do so: with Unions, and with Generics.

Unions:

type MyBool = true | false;

Note:  If you hover over MyBool above, you’ll see that it is classed as boolean. That’s a property of the Structural Type System结构类型系统. More on this below.

Generics: 泛型

type StringArray = Array<string>;


interface Backpack<Type> {

  add: (obj: Type) => void;

  get: () => Type;

}

// This line is a shortcut to tell TypeScript there is a

// constant called `backpack`, and to not worry about where it came from.

declare const backpack: Backpack<string>;

(4) Structural Type System

One of TypeScript’s core principles is that type checking focuses on the shape that values have. This is sometimes called “duck typing” or “structural typing”.In a structural type system, if two objects have the same shape, they are considered to be of the same type. The shape-matching only requires a subset of the object’s fields to match.

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

function logPoint(p: Point) {
  console.log(`${p.x}, ${p.y}`);
}

// logs "12, 26"
const point = { x: 12, y: 26 };
logPoint(point);

const point3 = { x: 12, y: 26, z: 89 };
logPoint(point3); // logs "12, 26" 不报错:only requires a `subset` of the object’s fields to match

(5) tsc错误

www.typescriptlang.org/docs/handbo…

一般情况下tsc错误后,仍然会输出js文件。如果想在有错误的情况下不输出可以使用这个命令:

tsc --noEmitOnError hello.ts

(6) downleveling

降级,可以编译成es3 es5语法,可以通过target调整

By default TypeScript targets ES3, an extremely old version of ECMAScript. We could have chosen something a little bit more recent by using the --target flag. Running with --target es2015 changes TypeScript to target ECMAScript 2015, meaning code should be able to run wherever ECMAScript 2015 is supported. So running tsc --target es2015 hello.ts gives us the following output: Template strings

(7) strictness

TypeScript has several type-checking strictness flags that can be turned on or off, and all of our examples will be written with all of them enabled unless otherwise stated. The --strict flag in the CLI, or "strict": true in a tsconfig.json toggles them all on simultaneously, but we can opt out of them individually.

The two biggest ones you should know about are noImplicitAny and strictNullChecks.

2.everyday-types

www.typescriptlang.org/docs/handbo…

(1)primitives: number string boolean

(2)Arrays: number[] 或者 Array

Note that [number] is a different thing; refer to the section on Tuples.

(3) any

(4) Functions

Much like variable type annotations, you usually don’t need a return type annotation because TypeScript will infer the function’s return type based on its return statements. The type annotation in the above example doesn’t change anything. Some codebases will explicitly specify a return type for documentation purposes, to prevent accidental changes, or just for personal preference.

Contextual typing for function : www.typescriptlang.org/docs/handbo…

(5)Object Types

function printName(obj: { x: number; y?: number }) {}

Here, we annotated the parameter with a type with two properties - x and y - which are both of type number. You can use , or ; to separate the properties, and the last separator is optional either way.

(6)Union Types

(7)Type Aliases

type alias is exactly that - a name for any type. The syntax for a type alias is:

type Point = {
  x: number;
  y: number;
};
type ID = number | string;

Note that aliases are only aliases - you cannot use type aliases to create different/distinct “versions” of the same type. When you use the alias, it’s exactly as if you had written the aliased type. In other words, this code might look illegal, but is OK according to TypeScript because both types are aliases for the same type:

type UserInputSanitizedString = string;

function sanitizeInput(str: string): UserInputSanitizedString {
  return sanitize(str);
}

// Create a sanitized input
let userInput = sanitizeInput(getInput());

// Can still be re-assigned with a string though
userInput = "new input";

(8)Interfaces

An interface declaration is another way to name an object type:

Being concerned only with the structure and capabilities of types is why we call TypeScript a structurally typed type system.

Differences Between Type Aliases and Interfaces

www.typescriptlang.org/docs/handbo…

Type aliases and interfaces are very similar, and in many cases you can choose between them freely. Almost all features of an interface are available in type, the key distinction is that a type cannot be re-opened to add new properties vs an interface which is always extendable.

For the most part, you can choose based on personal preference, and TypeScript will tell you if it needs something to be the other kind of declaration. If you would like a heuristic, use interface until you need to use features from type. 优先用interface

(9)Type Assertions

you can use a type assertion to specify a more specific type:

const myCanvas = document.getElementById("main_canvas") as HTMLCanvasElement;

You can also use the angle-bracket syntax (except if the code is in a .tsx file), which is equivalent:

const myCanvas = <HTMLCanvasElement>document.getElementById("main_canvas");

You can use as const to convert the entire object to be type literals:The as const suffix acts like const but for the type system, ensuring that all properties are assigned the literal type instead of a more general version like string or number.

const req = { url: "https://example.com", method: "GET" } as const;

(10) null and undefined

JavaScript has two primitive values used to signal absent or uninitialized value: null and undefined.How these types behave depends on whether you have the strictNullChecks option on. 推荐开启strictNullChecks

function doSomething(x: string | null) {
  if (x === null) {
    // do nothing
  } else {
    console.log("Hello, " + x.toUpperCase());
  }
}

Non-null Assertion Operator (Postfix!)非空断言,注意:Just like other type assertions, this doesn’t change the runtime behavior of your code, so it’s important to only use ! when you know that the value can’t be null or undefined.

function liveDangerously(x?: number | null) {
  // No error
  console.log(x!.toFixed());
}

(11)Enums

Enums are a feature added to JavaScript by TypeScript which allows for describing a value which could be one of a set of possible named constants. Unlike most TypeScript features, this is not a type-level addition to JavaScript but something added to the language and runtime. Because of this, it’s a feature which you should know exists, but maybe hold off on using unless you are sure. You can read more about enums in the Enum reference page. 会增加内容到runtime,谨慎使用

(12) Less Common Primitives: bigint symbol

3. Narrowing

Understand how TypeScript uses JavaScript knowledge to reduce the amount of type syntax in your projects. www.typescriptlang.org/docs/handbo…

ts通过几种方式能够判断一个值的更具体的类型

(1)typeoftype guards

string number bigint boolean symbol undefined object function

(2)Truthiness narrowing

In JavaScript, we can use any expression in conditionals, &&s, ||s, if statements, Boolean negations (!), and more. As an example, if statements don’t expect their condition to always have the type boolean.

(3)Equality narrowing

=== !== == !=

(4)The in operator narrowing

(5)instance narrowing

(6)Assignments

let x = Math.random() > 0.5 ? 10 : 'hello world'; // string | number
x = 1; // number
x = 'goodbye'; // string
x = true; // Type 'boolean' is not assignable to type 'string | number'.

Notice that each of these assignments is valid. Even though the observed type of x changed to number after our first assignment, we were still able to assign a string to x. This is because the declared type of x - the type that x started with - is string | number, and assignability is always checked against the declared type. x的类型取决于定义时候的类型,为string|number,可以assign为number则为number,这之后还是可以assign为string,但是不能assign为boolean等其它类型

(7) Control flow analysis

根据if while等语句,来分析

(8) Using type predicates

function isFish(pet: Fish | Bird): pet is Fish {
  return (pet as Fish).swim !== undefined;
}

// Both calls to 'swim' and 'fly' are now okay.
let pet = getSmallPet();
if (isFish(pet)) {
  pet.swim();
} else {
  pet.fly();
}

In addition, classes can use this is Type to narrow their type.

(9) Discriminated unions

interface Circle {
  kind: "circle";
  radius: number;
}

interface Square {
  kind: "square";
  sideLength: number;
}

type Shape = Circle | Square;

(10) The never type

Exhaustiveness checking

type Shape = Circle | Square;
function getArea(shape: Shape) {
  switch (shape.kind) {
    case "circle":
      return Math.PI * shape.radius ** 2;
    case "square":
      return shape.sideLength ** 2;
    default:
      const _exhaustiveCheck: never = shape;
    return _exhaustiveCheck;
  }
}

Adding a new member to the Shape union, will cause a TypeScript error:

interface Triangle {
    kind: "triangle";
    sideLength: number;
}

type Shape = Circle | Square | Triangle;
function getArea(shape: Shape) {
    switch (shape.kind) {
        case "circle":
            return Math.PI * shape.radius ** 2;
        case "square":
            return shape.sideLength ** 2;
        default:
            const _exhaustiveCheck: never = shape; // Type 'Triangle' is not assignable to type 'never'.Type 'Triangle' is not assignable to type 'never'.
        return _exhaustiveCheck;
    }
}

4. More on Functions

www.typescriptlang.org/docs/handbo…

(1) Function Type Expressions

type GreetFunction = (a: string) => void;

function greeter(fn: (a: string) => void) {
    fn("Hello, World");
}

(2) Call Signatures

type DescribableFunction = {
    description: string;
    (someArg: number): boolean;
};

(3) Construct Signatures

type SomeConstructor = {
    new (s: string): SomeObject;
};

interface CallOrConstruct {
    new (s: string): Date;
    (n?: number): number;
}

(4) Generic Functions

function firstElement<Type>(arr: Type[]): Type {
    return arr[0];
}

(5) Constraints

function longest<Type extends { length: number }>(a: Type, b: Type){}

(6) Function Overloads

function makeDate(timestamp: number): Date;    // 这是一个重载签名overload signatures
function makeDate(m: number, d: number, y: number): Date; // 这是一个重载签名overload signatures
function makeDate(mOrTimestamp: number, d?: number, y?: number): Date {  // 这是一个实现签名implementation signatures
    if (d !== undefined && y !== undefined) {
        return new Date(y, mOrTimestamp, d);
    } else {
        return new Date(mOrTimestamp);
    }
}

const d1 = makeDate(12345678);
const d2 = makeDate(5, 5, 5);
const d3 = makeDate(1, 3);

详细分析:juejin.cn/post/691230… 注意:

  • 因为implementation signatures对外是不可见的,当我们实现重载时,通常需要定义两个以上的overload signatures

  • implementation signature和 overload signature必须兼容,否则会 type check error 如下implementation 和 overload 不兼容

  • overload signature的类型不会合并,只能resolve到一个

(7) Declaringthisin a Function