TS入门指南看这篇就够了

149 阅读8分钟

string

let myName: string = "Alice";
// 或者
let myName = "Alice"; // 这种的话ts会自动推断类型为string

myName = 123 // 这种ts类型检查就会报错,他不是string类型

number

let age: number = 18;
// 或者
let age =18; // 这种的话ts会自动推断类型为number

age = '18' // 这种ts类型检查就会报错,他不是number类型

boolean

boolean只有true和false两种赋值

let isNumer: boolean = true;
// 或者
let isNumer =false; // 这种的话ts会自动推断类型为boolean

isNumer = '18' // 这种ts类型检查就会报错,他不是boolean类型

接口

接口声明是命名对象类型的另一种方式

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

interface Bear extends Person {
  honey: boolean;
}

类型别名

类型别名和接口非常相似,在许多情况下,您可以在它们之间自由选择。接口的几乎所有特性都可以在类型中使用,关键的区别在于类型不能重新打开以添加新属性,而接口总是可扩展的。

type Animal = {
  name: string;
}

type Bear = Animal & { 
  honey: boolean;
}
// 对比
interface Animal {
  name: string;
}

interface Bear extends Animal {
  honey: boolean;
}
 

Object

在JavaScript中,我们分组和传递数据的基本方式是通过对象。在TypeScript中,我们通过对象类型来表示它们。

定义对象类型

正如我们所看到的,它们可以是匿名的:

function greet(person: { name: string; age: number }) {
  return "Hello " + person.name;
}

或者通过接口来命名

interface Person {
  name: string;
  age: number;
}
 
function greet(person: Person) {
  return "Hello " + person.name;
}

或者类型别名

type Person = {
  name: string;
  age: number;
};
 
function greet(person: Person) {
  return "Hello " + person.name;
}

可选值

很多时候,我们会发现自己在处理可能有属性集的对象。在这些情况下,我们可以通过在它们的名称后面添加问号(?)来将这些属性标记为可选的

interface PaintOptions {
  shape: Shape;
  xPos?: number;
  yPos?: number;		
}
 
function paintShape(opts: PaintOptions) {
  // ...
}
 
const shape = getShape();
paintShape({ shape });
paintShape({ shape, xPos: 100 });
paintShape({ shape, yPos: 100 });
paintShape({ shape, xPos: 100, yPos: 100 });

只读

只读意味里面的属性不能改变,只能读取

interface ReadonlyPerson {
  readonly name: string;
  readonly age: number;
}

const readonlyPerson:ReadonlyPerson = {name:"123",age:18}
readonlyPerson.age = 12 // 这种就会提示错误,它是只读属性,不能改变

index、propName指数签名

有时,您无法提前知道类型属性的所有名称,但您确实知道值的形状。在这些情况下,您可以使用索引签名来描述可能值的类型,例如:

interface StringArray {
  [index: number]: string;
}
 
const myArray: StringArray =['1','2','3']
const secondItem = myArray[1];
    	
  	// const secondItem: string 

上面,我们有一个StringArray接口,它有一个索引签名。这个索引签名表明,当一个StringArray被一个数字索引时,它将返回一个字符串。

作为object的引用时,在下面的例子中,name的类型与字符串索引的类型不匹配,类型检查器给出了一个错误:

interface NumberDictionary {
  [index: string]: number;
 
  length: number; // ok
  name: string;
	// Property 'name' of type 'string' is not assignable to 'string' index type 'number'.
}

但是,如果索引签名是属性类型的联合,则可以接受不同类型的属性:

interface NumberOrStringDictionary {
  [index: string]: number | string;
  length: number; // ok, length is a number
  name: string; // ok, name is a string
}

但是,如果您确定对象可以具有一些以某种特殊方式使用的额外属性,那么更好的方法可能是添加字符串索引签名。除了拥有定义的类型之外,也可以拥有任意数量的其他属性,那么我们可以这样定义它:

interface SquareConfig {
  color?: string;
  width?: number;
  [propName: string]: any;
}

// 使用
const squareConfig:SquareConfig = {
  name:'123',
  length:12,
  age:48,
  color:'#fff',
  width:220
}

扩展类型

接口上的extends关键字允许我们有效地从其他命名类型复制成员,并添加我们想要的任何新成员。

interface BasicAddress {
  name?: string;
  street: string;
  city: string;
  country: string;
  postalCode: string;
}
 
interface AddressWithUnit extends BasicAddress {
  unit: string;
}

这时的AddressWithUnit就会拥有BasicAddress的所有成员和自己定义的新成员,不用在重复编写一些不必要的类型

接口也可以从多种类型扩展。

interface Colorful {
  color: string;
}
 
interface Circle {
  radius: number;
}
 
interface ColorfulCircle extends Colorful, Circle {}
 
const cc: ColorfulCircle = {
  color: "red",
  radius: 42,
};

交叉类型

TypeScript提供了另一个叫做交叉类型的结构,主要用于组合现有的对象类型。

interface Colorful {
  color: string;
}
interface Circle {
  radius: number;
  name:string
}
 
type ColorfulCircle = Colorful & Circle;

const cc: ColorfulCircle = {
  color: "red",
  radius: 42,
  name:"123"
};

联合类型

type Status = '1'|'2'|'3'

type Obj = {
  status:Status
}
let status:Status = '1'

数组

let list: number[] = [1, 2, 3];
let list: Array<number> = [1, 2, 3];

元组Tuple

元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。 比如,你可以定义一对值分别为string和number类型的元组。

// Declare a tuple type
let x: [string, number];
// Initialize it
x = ['hello', 10]; // OK
// Initialize it incorrectly
x = [10, 'hello']; // Error

元组解构

元组可以像数组一样被解构;解构变量获得相应元组元素的类型:

let tuple: [number, string, boolean] = [7, "hello", true];
let [a, b, c] = tuple; // a: number, b: string, c: boolean

与数组一样,您可以使用…来解构元组的其余部分。,以获得更短的元组:

let [a, ...bc] = tuple; // bc: [string, boolean]
let [a, b, c, ...d] = tuple; // d: [], the empty tuple

其他的元组定义方式

let tuple: [number, ...string[], boolean] = [7, "hello","hello2","hello3", true];

let tuple2: [number, boolean, ...string[],] = [7,  true,"hello","hello2","hello3",];

枚举

默认情况下,从0开始为元素编号。

enum Color {Red, Green, Blue}
let c: Color = Color.Green;

或者,全部都采用手动赋值:

enum Color {Red = 1, Green = 2, Blue = 4}
let c: Color = Color.Green;

任意值

有时候,我们会想要为那些在编程阶段还不清楚类型的变量指定一个类型。 这些值可能来自于动态的内容,比如来自用户输入或第三方代码库。 这种情况下,我们不希望类型检查器对这些值进行检查而是直接让它们通过编译阶段的检查。 那么我们可以使用any类型来标记这些变量:

let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean

unknown

未知类型表示任何值。这类似于any类型,但更安全,因为使用未知值做任何事情都是不合法的:

function f1(a: any) {
  a.b(); // OK
}
function f2(a: unknown) {
  a.b(); // 'a' is of type 'unknown'.
}

类型断言

有时候你会遇到这样的情况,你会比TypeScript更了解某个值的详细信息。 通常这会发生在你清楚地知道一个实体具有比它现有类型更确切的类型。

类型断言有两种形式。 其一是“尖括号”语法:

let someValue: any = "this is a string";

let strLength: number = (<string>someValue).length;

另一个为as语法:

let someValue: any = "this is a string";

let strLength: number = (someValue as string).length;

Keyof

keyof操作符接受一个对象类型,并产生其键的字符串或数字字面值联合。下面的类型P与类型P = "x" | "y"相同:

type Point = { x: number; y: number };
type P = keyof Point; // 等同于P = "x" | "y"
let a:P = 'x'
a='y'

泛型

软件工程的一个主要部分是构建不仅具有定义良好且一致的api,而且具有可重用性的组件。能够处理当前数据和未来数据的组件将为构建大型软件系统提供最灵活的功能。

function identity<T>(arg: T): T {
    return arg;
}

一旦我们写出了泛型恒等函数,我们可以用两种方式调用它。

第一种方法是将所有参数(包括类型参数)传递给函数:

let output = identity<string>("myString");

第二种方式可能也是最常见的。这里使用类型实参推断——也就是说,我们希望编译器根据传入的实参类型自动为我们设置type的值:

let output = identity("myString");

实用程序类型

TypeScript提供了几个实用程序类型来促进常见的类型转换。这些实用程序在全球范围内可用。

Partial

构造一个将type的所有属性设置为可选的类型。此实用程序将返回表示给定类型的所有子集的类型。

interface Todo {
  title: string;
  description: string;
}
type P = Partial<Todo> 
const todo1:P = {
  title: "clear clutter",
};

Required

构造由type set为required的所有属性组成的类型。Partial的反义词。必选类型

interface Props {
  a?: number;
  b?: string;
}
 
const obj: Props = { a: 5 };
 
const obj2: Required<Props> = { a: 5 };
// Property 'b' is missing in type '{ a: number; }' but required in type 'Required<Props>'.

Readonly

构造一个将type的所有属性设置为只读的类型,这意味着不能重新分配构造类型的属性。

interface Todo {
  title: string;
}
 
const todo: Readonly<Todo> = {
  title: "Delete inactive users",
};
 
todo.title = "Hello";
// Cannot assign to 'title' because it is a read-only property.

Record<Keys, Type>

构造一个对象类型,其属性键为keys,属性值为type。此实用程序可用于将一个类型的属性映射到另一个类型。

interface CatInfo {
  age: number;
  breed: string;
}
 
type CatName = "miffy" | "boris" | "mordred";
 
const cats: Record<CatName, CatInfo> = {
  miffy: { age: 10, breed: "Persian" },
  boris: { age: 5, breed: "Maine Coon" },
  mordred: { age: 16, breed: "British Shorthair" },
};

Pick<Type, Keys>

通过从type中选取一组属性Keys(字符串字面值或字符串字面值的并集)来构造一个类型。

interface Todo {
  title: string;
  description: string;
  completed: boolean;
}
 
type TodoPreview = Pick<Todo, "title" | "completed">;
 
const todo: TodoPreview = {
  title: "Clean room",
  completed: false,
};

Omit<Type, Keys>

通过从type中选取所有属性然后移除Keys(字符串字面值或字符串字面值的并集)来构造类型。Pick的反义词

interface Todo {
  title: string;
  description: string;
  completed: boolean;
  createdAt: number;
}
 
type TodoPreview = Omit<Todo, "description">;
 
const todo: TodoPreview = {
  title: "Clean room",
  completed: false,
  createdAt: 1615544252770,
};