Typescript的类型及类型和接口区别

[官方文档](TypeScript: JavaScript With Syntax For Types.)

> 以下是精简版本,如果不想看全篇看下面的内容就够了。

ts内置类型8个

  • Number
  • String
  • BigInt
  • Boolean
  • Symbol
  • Null
  • Undefined
  • Object

更多详情可以参考 [MDN](JavaScript 数据类型和数据结构 - JavaScript | MDN),

需要注意:原始类型都可以使用typeof进行测试,除了null,typeof null 返回'object',需要使用===null 来测试

其他重要的ts类型

  • unKnown
  • never
  • object literal eg{property:Type}
  • void for functions with no documented return value
  • T[] Array[T]
  • [T,T] tuples,
  • (t:T) => U functions

Notes:

1、方法的语法包含参数的名字,使用起来很难

let fast:(a:any,b:any)=>any=(a,b)=>a;
//更准确的
let fast:<T,U>(a:T,b:U)=>T=(a,b)=>a;

2、对象类型表明对象值的语法

let o:{n:number;xs:object[]} = {n:1,xs:[]};

Boxed types(盒装类型)

Javascript具有基本类型的盒装等价物,其中包含程序员与这些类似相关的方法。Typescript反映了这一点,例如,原始类型number和装箱类型Number之间的差异。很少需要盒装类型,因为他们的方法返回原始方法

(1).toExponential();
// equivalent to
Number.prototype.toExponential.call(1);

Gradual typing(渐进类型)

Typescript在无法确定表达式的类型时使用类型any.与Dynamic相比,将any称为类型就是言过其实了。它只是在出现的任何地方关闭类型检查器,使用noImplicitAny配置tsconfig.json


// tsconfig.json
{
  "compilerOptions": {
    "noImplicitAny": true
  }
}

Structural typing(结构类型)

结构类型是大多数函数是程序员比较熟悉的概念。

// @strict: false
let o = { x: "hi", extra: 1 }; // ok
let o2: { x: string } = o; // ok

下面可以使用类型起名,接口,以及类定义接口(但是,在递归定义和类型参数方面,类型别名的行为不同于接口)

type One = { p: string };
interface Two {
  p: string;
}
class Three {
  p = "Hello";
}
 
let x: One = { p: "hi" };
let two: Two = x;
two = new Three();

联合类型(|)

联合类型由两个及以上的成员构成,使用的时候值需是满足其中一种即可。

以下是内置的判断联合类型的条件

  • 字符串 typeof s === "string"
  • 数字 typeof n ==="number"
  • 大整数 typeof m === "bigint"
  • 布尔值 typeof b === "boolean"
  • 符号 typeof g === "symbol"
  • undefined typeof undefined === "undefined"
  • 函数 typeof f === "function"
  • 数组 Array.isArray(a)
  • 对象 typeof o === "object"
type LockStates = "locked" | "unlocked"
let state:LockStates = 'locked'

//注意以下内容的使用
let s = "right";
pad("hi",10,s) //此时s报错 error:'string' is not assignable to '"left"' | '"right"'

//更改使用 正确用法
let s:"left" | "right" = "right";
pad("hi",10,s)

交叉类型(&)

交叉类型可以将多个类型合并为一个类型,合并后的类型拥有所有类型的属性和方法。

type Combined = { a: number } & { b: string };
type Conflicting = { a: number } & { a: string };

注意:Conflicting a有两种类型一种是number一种是string,本没有这种类型的所以被解析出来是never类型。

Contextual typing 上下文类

Typescript有一些明显的地方可以类型推断。

declare function map<T, U>(f: (t: T) => U, ts: T[]): U[];
let sns = map((n) => n.toString(), [1, 2, 3]);

此处,此示例中的 n: number 也是如此,尽管在调用之前尚未推断出 T 和 U。 实际上,在使用 [1,2,3] 推断 T=number 之后,n => n.toString() 的返回类型被用来推断 U=string,导致 sns 的类型为 string[]。

declare function run<T>(thunk: (t: T) => void): T;
let i: { inference: string } = run((o) => {
  o.inference = "INSERT STATE HERE";
});

o 的类型被确定为{inference:string}因为:

  1. 声明初始值设定项根据声明的类型进行上下文类型化: { inference: string }。
  2. 调用的返回类型使用上下文类型进行推断,因此编译器推断 T={ inference: string }。
  3. 箭头函数使用上下文类型来键入它们的参数,因此编译器给出了 o: { inference: string }。

类型别名

类别名称只是别名,会给类型起一个新的名字

  • 基础案例
type Size = [number,number]
let x:Size = [122,125]
  • 通过&创建新的类型
type FString = string & { __compileTimeOnly: any };

FString就像普通的字符串,只是编译器认为他有一个名为__compileTimeOnly的属性,但实际上并不存在,这意味着FString仍然可以分配给string,但反之则不行。

接口interface和类型的区别

注意:类型别名和接口interface非常相似,很多情况下你可以在他们之间自由选择。interface的几乎所有功能type中可以使用,主要区别在于无法重新打开类型添加新属性,而接口始终可以可以扩展。

  • 类别名称不能参与声明合并,接口可以

两者如何实现扩展

  • 接口只能用于声明对象的类型,不能重命名基本类型

建议:在多数情况下,可以根据个人喜好进行选择,一般情况下都用interface,如果不满足可使用type

判别联合

与data最接近的等效项是具有判别属性的类型联合

type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "square"; x: number }
  | { kind: "triangle"; x: number; y: number };

标记或判别只是每个对象类型中的属性,每个变体都具有相同属性和不同的单元类型,这仍然是一个普通的联合类型;前导 | 是联合类型语法的可选部分。可以通过javascript代码来区分联合体的成员

type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "square"; x: number }
  | { kind: "triangle"; x: number; y: number };
 
function area(s: Shape) {
  if (s.kind === "circle") {
    return Math.PI * s.radius * s.radius;
  } else if (s.kind === "square") {
    return s.x * s.x;
  } else {
    return (s.x * s.y) / 2;
  }
}

上面的代码area的返回类型被推断为number,默认为完整代码,如果没有涉及某些结果可以将返回类型修改为 number|undefined

类型参数

函数的参数也可以需要声明类型的。没有大小写要求,但类型通常是单个大写字母。类型参数也可以被约束为一个类型。有点像类型约束。

function firstish<T extends { length: number }>(t1: T, t2: T): T {
  return t1.length > t2.length ? t1 : t2;
}

因为Typescript是结构化的,所以它不像名义系统那样需要类型参数。具体来说,不需要他们来时函数多态。类型参数应该只用于传播类型信息,例如约束参数为同一类型

//T不是必须的;因为它只被引用了一次,所以它没有被用来约束返回值或其他参数类型。
function length<T extends ArrayLike<unknown>>(t: T): number {}
function length(t: ArrayLike<unknown>): number {}

模块系统

通过import导入export导出

//导入
import { value, Type } from "npm-package";
import * as prefix from "../lib/third-package";
import f = require("single-function-package"); //通过commonjs模块 使用node.js模块

readonly和const

  • const的应用

在Javascript中,尽管它允许带有const的变量声明,声明引用时不可变的,引用仍是可以变得

const a = [1, 2, 3];
a.push(102); // ):
a[0] = 101; // D:

针对对象或者数组使用const进行断言。

//数组断言
let a = [1, 2, 3] as const;
a.push(102); // error
a[0] = 101; // error

//对象断言(优点:在使用的时候可以清楚的看到属性内容)
const test = {
  name:'124',
  age:13
} as const
  • readonly的应用
//属性的使用
interface Rx{
   readoly x:number
}

let rx: Rx = { x: 1 };
rx.x = 12; // error

//接口的使用
interface X {
  x: number;
}
let rx: Readonly<X> = { x: 1 };
rx.x = 12; // error

//数组的使用
let a: ReadonlyArray<number> = [1, 2, 3];
let b: readonly number[] = [1, 2, 3];
a.push(102); // error
b[0] = 101; // error

非空断言运算符(后缀!)

Typescript还具有一种特殊的语法,可以在不进行热河显示检查的情况下从类型中删除null和undefined.在热河表达时候写!实际上是一个类型断言,该值不是null或undefined

function liveDangerously(x?: number | null) {
  // No error
  console.log(x!.toFixed());
}
//当我们知道x不为null或者undefined 使用!就很重要

书写规范

  • 有些情况,typescript可以为我们推断出类型,建议不要自己手动在填上类型。

  • 使用noImplicitAny来控制any
  • 使用strictNullChecks 来控制undefined或者null

常见问题

  • 类型断言
declare function handleRequest(url: string, method: "GET" | "POST"): void;
 
const req = { url: "https://example.com", method: "GET" };
handleRequest(req.url, req.method);

//Argument of type 'string' is not assignable to parameter of type '"GET" | "POST"'.

解决方法一:

//明确类型为GET
handleRequest(req.url, req.method as "GET"); 

解决方法二

//as const 后缀的作用类似用从上图,但用于类型系统,确保所有书信分配字面量类型。而不是通用版本,如string或number
const req = { url: "https://example.com", method: "GET" } as const;
handleRequest(req.url, req.method);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
TypeScript 中的接口类型别名都可用于定义类型,但在一些情况下,它们有不同的使用场景。 接口用于描述对象的形状,可以用来定义对象的属性、方法和方法的参数类型。例如: ```typescript interface Person { name: string; age: number; sayHello: () => void; } const person: Person = { name: 'Tom', age: 18, sayHello() { console.log(`Hello, my name is ${this.name}`); } }; ``` 类型别名用于给一个类型定义一个新的名字,可以用来定义基本类型、联合类型、交叉类型等。例如: ```typescript type Age = number; type Name = string; type Person = { name: Name; age: Age; }; const person: Person = { name: 'Tom', age: 18, }; ``` 需要注意的是,接口类型别名都可以用于描述函数类型: ```typescript interface Add { (a: number, b: number): number; } type Sub = (a: number, b: number) => number; const add: Add = (a, b) => a + b; const sub: Sub = (a, b) => a - b; ``` 但是,接口类型别名在描述函数类型时有所不同。接口可以描述函数的可选参数、默认参数和剩余参数,而类型别名则不能。例如: ```typescript interface Func { (a: number, b?: number, ...rest: number[]): void; } type FuncAlias = (a: number, b?: number, ...rest: number[]) => void; const func: Func = (a, b, ...rest) => { console.log(a, b, rest); }; const funcAlias: FuncAlias = (a, b, ...rest) => { console.log(a, b, rest); }; ``` 因此,在需要描述函数类型时,建议优先使用接口

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值