后端上手学习TypeScript基础知识

前言

java语言对于数据的处理,有各种类型的约束,在js中没有类型约束,我们每次在写代码的时候经常不确定书写的格式类型,编译器无法及时的判断我们使用的正确与否,所以增加了TypeScript就可以使得我们更稳健,更牢靠的相信我们写下去的代码。

基本类型

  • boolean : 布尔类型true/false
  • string : 字符串
  • number : 数字类型,整数/小数
  • bigint : 大整数
  • symbol :类似于字符串,但独一无二,Symbol()函数生成
  • object : 对象
  • undefined :未定义
  • null : 为空

TypeScript 继承了 JavaScript 的类型设计,以上8种类型可以看作 TypeScript 的基本类型。

typescript 复制代码
const x:string = 'hello'; // 定义字符串 foo 为string 类型
const x:boolean = true; // 定义boolean类型
const x:number = 123; // 数字整数类型
const y:number = 3.14; // 数字浮点类型
const x:bigint = 123n; // 大整数类型
const x:object = { foo: 123 } //对象类型

// 特殊场景 undefined 和 null
let x:undefined = undefined;
const x:null = null;

Object和object区别

Object类型代表 JavaScript 语言里面的广义对象, 注意: 但除了undefinednull这两个值不能转为对象

ini 复制代码
obj = true;
obj = 'hi';
obj = 1;
obj = { foo: 123 };
obj = [1, 2];
obj = (a:number) => a + 1;

let obj:Object;
obj = undefined; // 报错
obj = null; // 报错

object类型代表 JavaScript 里面的狭义对象, 只包含对象、数组和函数,不包括原始类型的值

ini 复制代码
let obj:object;
 
 // 可以是对象,数组,函数
obj = { foo: 123 };
obj = [1, 2];
obj = (a:number) => a + 1;

obj = true; // 报错
obj = 'hi'; // 报错
obj = 1; // 报错

值/联合类型(|)

值类型: 单个值的类型

csharp 复制代码
let x:'hello';  // 类型是字符串hello,无法赋值其他字符串值
const x = 'https'; // 推断类型为https,因const一旦声明就不能改变

联合类型 : 多个类型组成的一个新类型,使用符号|表示。用A|B表示,任何一个类型只要属于AB,就属于联合类型A|B

ini 复制代码
let x:string|number; // x既可以是字符串类型,也可以是数字类型
x = 'sdf'
x = 123

通过联合类型可以与值类型相结合,表示一个变量的值有若干种可能

bash 复制代码
let gender:'male'|'female';
let setting:true|false;

交叉类型(&)

指的多个类型组成的一个新类型, 主要用途是表示对象的合成,表明同时具有几种属性

csharp 复制代码
let obj: { foo: string } & { bar: string }; //即有foo,又有bar

obj = {
  foo: 'hello',
  bar: 'world'
}; 

type(定义别名)

可以用于定义一个类型的别名,如下用于了将number类型命名为Age别名,更具有意义。

ini 复制代码
type Age = number;

let age:Age = 55;

注意: 不能重复声明,作用域只在块级作用域。

一般使用的时候通过定义对象模板

ini 复制代码
type Country = {
  name: string;
  capital: string;
}

const country: Country={name:'gjc',capital:'sdf'}

有关type的扩展类型需要借助 交叉类型

go 复制代码
type MyStr = string & {type: 'new'};

keyof 运算符

返回联合字面量类型

lua 复制代码
interface Person {
    name: string
    age: number
    location: string
}

type SomeNewType = keyof Person // "name" | "age" | "location"

typeof运算符

JavaScript 语言中,typeof 运算符是一个一元运算符,返回一个字符串,代表操作数的类型

javascript 复制代码
typeof undefined; // "undefined"
typeof true; // "boolean"
typeof 1337; // "number"
typeof "foo"; // "string"
typeof {}; // "object"
typeof parseInt; // "function"
typeof Symbol(); // "symbol"
typeof 127n // "bigint"

在ts中,返回ts类型

ini 复制代码
const a = { x: 0 };

type T0 = typeof a;   // { x: number }
type T1 = typeof a.x; // number

特殊类型

  1. any类型 : 表示没有任何限制,该类型的变量可以赋予任意类型的值
  2. unknown 类型 : 类型不确定,是严格版的any,只有明确unknown变量的实际类型,才允许使用它
  3. never 类型 : 该类型为空,不包含任何值 , 逻辑处理分支的兜底使用
ini 复制代码
let x:any;
x = 1; // 正确
x = 'foo'; // 正确
x = true; // 正确

let x:unknown;
x = true; // 正确
x = 42; // 正确
x = 'Hello World'; // 正确
let v1:unknown = { foo: 123 };
v1.foo  // 报错, 无法使用

// 明确缩小类型可以用
let a:unknown;
if (typeof a === 'number') {
  let r = a + 10; // 正确
}

// 兜底使用
function fn(x:string|number) {
  if (typeof x === 'string') {
    // ...
  } else if (typeof x === 'number') {
    // ...
  } else {
    x; // never 类型
  }
}

数组

数组的类型有两种写法:

  1. 第一种写法是在数组成员的类型后面,加上一对方括号。
  2. 第二种写法 是使用 TypeScript 内置的 Array 接口

数组声明

  • 指定类型的数组形式

    jsx 复制代码
    let arr:number[] = [1, 2, 3];
  • 指定多个类型的可能性的形式(联合类型)

    jsx 复制代码
    let arr:(number|string)[];
  • 任意类型的数组 ,any

    jsx 复制代码
    let arr:any[];
  • Array 写法

    jsx 复制代码
    let arr:Array<number> = [1, 2, 3];
    let arr:Array<number|string>;

数组类型推断

如果数组变量没有声明类型,TypeScript 就会推断数组成员的类型。这时,推断行为会因为值的不同,而有所不同。

jsx 复制代码
const arr = [];
arr // 推断为 any[]

arr.push(123);
arr // 推断类型为 number[]

arr.push('abc');
arr // 推断类型为 (string|number)[]

注意: 数组有了初始值,就会推断出指定的类型,其他类型不允许了

jsx 复制代码
// 推断类型为 number[]
const arr = [123];

arr.push('abc'); // 报错

只读数组

TypeScript 允许声明只读数组,方法是在数组类型前面加上readonly关键字

jsx 复制代码
const arr:readonly number[] = [0, 1];

arr[1] = 2; // 报错
arr.push(3); // 报错
delete arr[0]; // 报错

元组

元组(tuple)是 TypeScript 特有的数据类型,JavaScript 没有单独区分这种类型。它表示成员类型可以自由设置的数组,即数组的各个成员的类型可以不同

php 复制代码
const s:[string, string, boolean]  = ['a', 'b', true];

与数组不同的是

成员类型写在方括号里面的就是元组,写在外面的就是数组

less 复制代码
// 数组
let a:number[] = [1];

// 元组
let t:[number] = [1];

成员可选 ?

元组成员的类型可以添加问号后缀(?),表示该成员是可选的

注意 :所有可选成员必须在必选成员之后

ini 复制代码
let a:[number, number?] = [1];

不确定数量

typescript 复制代码
type t1 = [string, number, ...boolean[]];

对象

声明对象的类型

对象类型的最简单声明方法,就是使用大括号表示对象,在大括号内部声明每个属性和方法的类型

jsx 复制代码
const obj:{x:number;y:number;} = { x: 1, y: 1 };

注意: 一旦声明了类型,对象赋值时,就不能缺少指定的属性,也不能有多余的属性,同样地,也不能删除类型声明中存在的属性,修改属性值是可以的

可选属性 ?

如果某个属性是可选的(即可以忽略),需要在属性名后面加一个问号。

jsx 复制代码
const obj: {
  x: number;
  y?: number;
} = { x: 1 };

只读属性 readonly

声明对象中的age属性为只读

jsx 复制代码
const person:{
  readonly age: number
} = { age: 20 };

person.age = 21; // 报错

解构赋值

从对象中提取属性的同时,追加其类型

jsx 复制代码
const {id, name, price}:{
  id: string;
  name: string;
  price: number
} = product;

函数

function函数

定义函数的入参类型和出参类型

typescript 复制代码
// 声明入参num为number类型,出参为string类型
function toString(num:number):string {
  return String(num);
}

注意: 可以不定义入参num的类型,ts能推断出类型,没有足够信息,推断为any类型,返回值也可以不写,ts能推断出来

箭头函数

typescript 复制代码
// 箭头函数形式的 函数类型
const IdGenerator = (name: string, id: number) => string {
	return name + id;
};

=====> 等同于 function函数

function IdGenerator (name: string, id: number): string {
  return name + id;
}

函数可选参数 ?

scss 复制代码
function f(x?:number) {
  // ...
}

f(); // OK
f(10); // OK

参数x后面有问号,表示该参数可以省略, 该参数的类型实际上是原始类型|undefined

函数参数默认值 =

设置了默认值的参数,就是可选的。如果不传入该参数,它就会等于默认值

注意: 可选参数和默认值不能同时使用

typescript 复制代码
function createPoint(x:number = 0, y:number = 0):[number, number] {
  return [x, y];
}

函数参数结构 {} /[]

传入的变量里面是对象/数组的,可以定义一一对应的解构

typescript 复制代码
// 对象解构
function sum( { a, b, c }: { a: number; b: number; c: number } ) { 
}

// 数组解构
function f([x, y]: [number, number]) {
}

rest 参数 (剩余)

rest 参数表示函数剩余的所有参数,它可以是数组(剩余参数类型相同),也可能是元组(剩余参数类型不同)。

php 复制代码
// rest 参数为数组
function joinNumbers(...nums:number[]) {
  // ...
}

// rest 参数为元组
function f(...args:[boolean, number]) {
  // ...
}

只读 readonly

如果函数内部不能修改某个参数,可以在函数定义时,在参数类型前面加上readonly关键字,表示这是只读参数。

php 复制代码
function arraySum(arr:readonly number[]) {
  arr[0] = 0; // 报错
}

never 类型(兜底)

函数fail()会抛出错误,不会正常退出,所以返回值类型是never

typescript 复制代码
function fail(msg:string):never {
  throw new Error(msg);
}

函数sing()会永远执行,不会返回,所以返回值类型是never

javascript 复制代码
const sing = function():never {
  while (true) {
    console.log('sing');
  }
};

interface

interface 是对象的模板,可以看作是一种类型约定,中文译为"接口"。使用了某个模板的对象,就拥有了指定的类型结构

css 复制代码
interface Person {
  firstName: string;
  lastName: string;
  age: number;
}

=======> 对应使用

const p:Person = {
  firstName: 'John',
  lastName: 'Smith',
  age: 25
};

interface 可以表示对象的各种语法,属性

对象属性

x和y都是对象的属性,分别使用冒号指定每个属性的类型

typescript 复制代码
interface Point {
  x: number;
  y: number;
  b?: string; // 可选
  readonly a: string; // 只读
}

对象的属性索引

属性索引共有stringnumbersymbol三种类型。

注意: 一个接口中,最多只能定义一个字符串索引 , 字符串索引会约束该类型中所有名字为字符串的属性,一个接口中最多也只能定义一个数值索引, 如果同时定义了字符串索引和数值索引,那么数值索引必须服从于字符串索引

typescript 复制代码
interface A {
  [prop: string]: number;  // 约束属性名称为字符串,对应属性值得是number
  a: boolean;      // 编译错误
  b:1              //正确 
}

interface A {
  [prop: number]: string;   //  约束属性名称为数值,对应属性值得是字符串
}
const obj:A = ['a', 'b', 'c'];  //对应数组就是,名称是数值,值是字符串

// 数值索引必须兼容字符串索引的类型声明,都是number
interface B {
  [prop: string]: number;
  [prop: number]: number; // 正确
}

对象方法

typescript 复制代码
// 写法一
interface A {
  f(x: boolean): string;
}
// 写法二
interface B {
  f: (x: boolean) => string;
}
// 写法三
interface C {
  f: { (x: boolean): string };
}

// 对应的使用
const i : interface={
    f:(x: boolean) : string => return ` ${x} hello`
}

独立函数模板

接口Add声明了一个函数类型

typescript 复制代码
interface Add {
  (x:number, y:number): number;
}

const myAdd:Add = (x,y) => x + y;

继承 extends

extends关键字会从继承的接口里面拷贝属性类型,这样就不必书写重复的属性。interface 允许多重继承

注意: 如果子接口与父接口存在同名属性,那么子接口的属性会覆盖父接口的属性。子接口与父接口的同名属性必须是类型兼容的

typescript 复制代码
interface Shape {
  name: string;
}

interface Circle extends Shape {
  radius: number;
}

interfacetype 的异同

很多对象类型即可以用 interface 表示,也可以用 type 表示。而且,两者往往可以换用,几乎所有的 interface 命令都可以改写为 type 命令

主要区别:

  • type能够表示非对象类型,而interface只能表示对象类型(包括数组、函数等)。
  • interface可以继承其他类型,type不支持继承, 注意继承可以相互换用的,interface 可以extends实现typetype可以&来扩展interface
  • 同名interface会自动合并,同名type则会报错。
  • interface不能包含属性映射(mapping),type可以
  • this关键字只能用于interface
  • type 可以扩展原始数据类型,interface 不行
  • interface无法表达某些复杂类型(比如交叉类型和联合类型),但是type可以
ini 复制代码
// 同名`interface`会自动合并
interface A { foo:number };
interface A { bar:number };
const obj:A = {foo: 1,bar: 1};

// `type` 可以扩展原始数据类型
type MyStr = string & {
  type: 'new'
};

// type表达某些复杂类型
type A = { /* ... */ };
type B = { /* ... */ };
type AorB = A | B;

使用上的抉择:

如果有复杂的类型运算,那么没有其他选择只能使用type;一般情况下,interface灵活性比较高,便于扩充类型或自动合并,建议优先使用

泛型

使用泛型的主要目的是为了处理不特定类型的数据,使得代码可以适用于多种数据类型而不失去类型检查

泛型函数

csharp 复制代码
function identity<T>(arg: T): T {
    return arg;
}

// 使用泛型函数
let result = identity<string>("Hello");
let numberResult = identity<number>(42);

泛型接口

sql 复制代码
interface Pair<T, U> {
    first: T;
    second: U;
}

// 使用泛型接口
let pair: Pair<string, number> = { first: "hello", second: 42 };
console.log(pair); // 输出: { first: 'hello', second: 42 }

泛型约束

限制泛型的类型范围,泛型实现指定的类型

typescript 复制代码
interface Lengthwise {
    length: number;
}

function logLength<T extends Lengthwise>(arg: T): void {
    console.log(arg.length);
}
logLength({length:12})

泛型默认值

可以给泛型设置默认值,使得在不指定类型参数时能够使用默认类型

ini 复制代码
function defaultValue<T = string>(arg: T): T {
    return arg;
}

// 使用带默认值的泛型函数
let result1 = defaultValue("hello");

枚举

把相关的常量放在一个容器里。

数值枚举

ini 复制代码
enum Color {
  Red,     // 0
  Green,   // 1
  Blue     // 2
}

let c = Color.Red // 0
let c1:Color = Color.Red;
let c2:Color = Color.Green;
let c3:number = Color.Red

注意: 由于 Enum 结构编译后是一个对象,所以不能有与它同名的变量(包括对象、函数、类等)

Enum 成员值都是只读的,不能重新赋值

arduino 复制代码
const enum Color {
  Red,     // 0
  Green,   // 1
  Blue     // 2
}

字符串枚举

可以定义一组相关字符串的集合

ini 复制代码
enum Direction {
  Up = 'UP',
  Down = 'DOWN',
  Left = 'LEFT',
  Right = 'RIGHT',
}

let s = Direction.Up; // UP
let s1:Direction = Direction.Up

keyof 运算符搭配

keyof 运算符可以取出 Enum 结构的所有成员名,作为联合类型返回。可以和typeof搭配使用获取枚举 。不能直接使用 keyof xxEnum

ini 复制代码
enum MyEnum {
  A = 'a',
  B = 'b'
}

// 'A'|'B'
type Foo = keyof typeof MyEnum;

typeof MyEnum会将MyEnum当作一个值处理,从而先其转为对象类型,就可以再用keyof运算符返回该对象的所有属性名

反向映射

数值Enum 存在反向映射,即可以通过成员值获得成员名 ,注意字符串Enum不行

ini 复制代码
enum Direction{
    UP = 1,
    DOWN = 2,
}

Direction[Direction.UP] // 'UP'

模块

import

在a文件中导出一个类型模板和变量

typescript 复制代码
// a.ts
export interface A {
  foo: string;
}

export let a = 123;

在b文件中导入有两种方法,第一种是直接在对于类型前面加上type关键字,第二种就是单独使用导入类型

typescript 复制代码
import { type A, a } from './a'; //第一种
import type { A } from './a'; // 第二种

这么做是为了区分类型还是正常接口

export

导出也是分两种写法,一种写在一起type,一种是分开分别type

bash 复制代码
type A = 'a'
type B = 'b'

export type {A,B}
export {type A,type B}

搭配React的使用

函数组件

声明类型对象模板,为了约束函数组件接受上层组件的传值props的类型

typescript 复制代码
interface IProps {
  name: string
}

const App = (props: IProps) => {
  const {name} = props;
  return (
    <div>{name}</div>
  );
}

export default App;

使用 React.FunctionComponent<P={}> 来定义,简写为React.FC<P={}> ,使用它可以 自动推断 children 属性 ,不用手动声明

javascript 复制代码
// 重写上面的案例
const App:React.FC<IProps> (props) => {
  const {name} = props;
  return (
    <div>{name}</div>
  );
}

React 内置类型

  • JSX.Element : 表示一个 JSX 元素
javascript 复制代码
const jsx = <div>hello</div>
  • React.ReactElement : 表示一个 JSX 元素
ini 复制代码
const element: React.ReactElement = <div>Hello World</div>;
  • React.ReactNode : 表示任何可以被渲染的内容,包括 JSX 元素、字符串、数字、null、布尔值等
ini 复制代码
const content: React.ReactNode = 'This is a text node';
const element: React.ReactNode = <div>{content}</div>;

Hooks

useState: 声明的时候定义类型 , 如果初始值为null,需要显式地声明 state 的类型

typescript 复制代码
const [count, setCount] = useState<number>(1)
const [person, setPerson] = useState<Person>({ name:"gjc", age:1 });
const [count, setCount] = useState<number|null>(null); 

useRef : 引用dom元素

csharp 复制代码
// inputEl 只能用于 input elements
const inputEl = React.useRef<HTMLInputElement>(null);
// TS 会检查 inputEl 类型, current需要自行判断
inputEl.current?.focus()
相关推荐
qq. 28040339845 小时前
CSS层叠顺序
前端·css
喝拿铁写前端5 小时前
SmartField AI:让每个字段都找到归属!
前端·算法
猫猫不是喵喵.5 小时前
vue 路由
前端·javascript·vue.js
烛阴6 小时前
JavaScript Import/Export:告别混乱,拥抱模块化!
前端·javascript
bin91536 小时前
DeepSeek 助力 Vue3 开发:打造丝滑的表格(Table)之添加行拖拽排序功能示例12,TableView16_12 拖拽动画示例
前端·javascript·vue.js·ecmascript·deepseek
GISer_Jing6 小时前
[Html]overflow: auto 失效原因,flex 1却未设置min-height &overflow的几个属性以及应用场景
前端·html
程序员黄同学7 小时前
解释 Webpack 中的模块打包机制,如何配置 Webpack 进行项目构建?
前端·webpack·node.js
拉不动的猪7 小时前
vue自定义“权限控制”指令
前端·javascript·vue.js
再学一点就睡7 小时前
浏览器页面渲染机制深度解析:从构建 DOM 到 transform 高效渲染的底层逻辑
前端·css
拉不动的猪7 小时前
刷刷题48 (setState常规问答)
前端·react.js·面试