TS(一) 类型检测

TS

一、类型检测

0、type定义类型别名,方便多次复用

// type用于定义类型别名(type alias)
type IDType = string | number | boolean
type PointType = {
  x: number
  y: number
  z?: number
}
function printId(id: IDType) {

}
function printPoint(point: PointType) {
  
}

1、number、string、boolean 类型

	let name: string = "why";
	let num1: number = 100;
	let flag: boolean = true;

2、array类型

  • array类型里只能放同一种类型,如:string[ ]代表数组里只能放string类型
	const names1: Array<string> = []; // 不推荐
	const names2: string[] = []; // 推荐

3、tuple类型

  • tuple类型里可以放多种类型,但类型需要一一对应
	const info: [string, number, number] = ["why", 18, 1.88]; // 类型需要	一一对应
	const name = info[0];
	console.log(name.length);
	const age = info[1];
	console.log(age.length); // 这里会报错,因为array类型无length方法
  • 因为tuple里面可以放多个类型,所以比较适合作为函数的返回值
	// [any, (newState: any) => void] 为返回值类型
	function useState(state: any): [any, (newState: any) => void] {
	  let currentState = state;
	  const changeState = (newState: any) => {
	    console.log(newState);
 	 };
 	 return [currentState, changeState];
	}

4、object类型

	const info: {
 	 name: string;
 	 age: number;
	} = {
 	 name: "why",
 	 age: 18,
	};
	console.log(info.name);

5、null 和 undefined

	// null只能赋值null undefined只能赋值undefined
	let n1: null = null;
	let n2: undefined = undefined;
  • unknown的使用
	let flag = true;
	let result: unknown; // 最好不要使用any
	if (flag) {
 	 result = 123;
	} else {
	  result = "abc";
	}
	console.log(result);

6、Symbol类型

	const title1 = Symbol("title");
	const title2 = Symbol("title");

	const info = {
	  [title1]: "程序员",
	  [title2]: "老师",
	};
	// 取值
	console.log(info[title1]);

7、void类型

函数无返回值的类型就是void
	// 默认就是void
	function sum(num1: number, num2: number) {
 	 	console.log(num1 + num2);
	}

8、never类型

never类型不匹配任何类型,所以赋任何类型的值都会报错
function handleMessage(message: string | number | boolean) {
  switch (typeof message) {
    case "string":
      console.log("string处理方式处理message");
      break;
    case "number":
      console.log("number处理方式处理message");
      break;
    // 防止给message增加类型后忘记编写相关代码
    // 打开以下备注 就不会报错;
    // case "boolean":
    //   console.log("boolean处理方式处理message");
    //   break;
    default:
      // never 类型不存在赋值过程  所以当未匹配的boolean类型的message赋值给check时会报错
      const check: never = message;
  }
}

handleMessage(true); //调用未匹配的boolean类型

9、any类型

在不想给某些JavaScript添加具体的数据类型时,可以使用any类型,便会跳过类型检测,和原生的JavaScript代码是一样的;
	let message: any = "Hello World";
	message = 123;
	message = true;
	message = {};

10、字面量类型

 字面量类型 ,值和类型是保持一致的,常结合联合类型使用
type Method = "GET" | "POST";  // 这样Method只能为这两个值
type Request = {
  url: string;
  method: Method;
};
function request(url: string, method: Method) {}
const options: Request = {
  url: "https://www.coderwhy.org/abc",
  method: "POST",
};

11、函数相关的类型

  • 函数的参数和返回值都需要确定类型,参数必须明确写出类型,返回值默认会自动推导;
function sum(num1: number, num2: number):number {
  return num1 + num2
}
// 参数也可以为对象,函数,相应的需要确定类型
function printPoint(point: {x: number, y: number}) {
  console.log(point.x);
  console.log(point.y)
}
printPoint({x: 123, y: 321})
  • 匿名函数的类型推导
const names = ["abc", "cba", "nba"]
// item根据上下文的环境推导出来的, 这个时候可以不添加的类型注解
// 上下文中的函数: 可以不添加类型注解
names.forEach(function(item) {
  console.log(item.split(""))
})
  • 对象类型也可以指定哪些属性是可选的,可以在属性的后面添加一个“?”
function printPoint(point: {x: number, y: number, z?: number}) {
  console.log(point.x)
  console.log(point.y)
  console.log(point.z)
}
printPoint({x: 123, y: 321})
printPoint({x: 123, y: 321, z: 111})
  • 联合类型
function printID(id: number | string | boolean) {
  // 使用联合类型的值时, 需要特别的小心
  // narrow: 缩小
  if (typeof id === "string") {
    // TypeScript帮助确定id一定是string类型
    console.log(id.toUpperCase());
  } else {
    console.log(id);
  }
}
printID(123);
printID("abc");
printID(true);

使用联合类型后因为一个参数可以取多个类型,所以需要先确定类型(即缩小类型),才能调用每个类型独有的方法与属性;
下面是类型缩小的几种方式:

// 1.typeof的类型缩小
type IDType = number | string;
function printID(id: IDType) {
  if (typeof id === "string") {
    console.log(id.toUpperCase());
  } else {
    console.log(id);
  }
}

// 2.平等的类型缩小(=== == !== !=/switch)
type Direction = "left" | "right" | "top" | "bottom";
function printDirection(direction: Direction) {
  // 1.if判断
  // if (direction === 'left') {
  //   console.log(direction)
  // } else if ()
  // 2.switch判断
  // switch (direction) {
  //   case 'left':
  //     console.log(direction)
  //     break;
  //   case ...
  // }
}

// 3.instanceof
function printTime(time: string | Date) {
  if (time instanceof Date) {
    console.log(time.toUTCString());
  } else {
    console.log(time);
  }
}

class Student {
  studying() {}
}

class Teacher {
  teaching() {}
}

function work(p: Student | Teacher) {
  if (p instanceof Student) {
    p.studying();
  } else {
    p.teaching();
  }
}

const stu = new Student();
work(stu);

// 4. in
type Fish = {
  swimming: () => void;
};

type Dog = {
  running: () => void;
};

function walk(animal: Fish | Dog) {
  if ("swimming" in animal) {
    animal.swimming();
  } else {
    animal.running();
  }
}

const fish: Fish = {
  swimming() {
    console.log("swimming");
  },
  // 不能加running方法
  // running(){
  //   console.log("running")
  // }
};

walk(fish);

12、类型断言(缩小类型范围)

  • 使用DOM元素特有的属性和方法时
const el = document.getElementById("why") as HTMLImageElement;
el.src = "url地址";
  • 使用子类特有的属性和方法时
class Person {}

class Student extends Person {
  studying() {}
}

function action(p: Person) {
  // p.studying()  会报错
  (p as Student).studying(); // 只有学生才能调用studying
}

const stu = new Student();
action(stu);

二、各种操作符

1、! 非空断言操作符

 x! 将从 x 值域中排除 null 和 undefined;
function myFunc(maybeString: string | undefined | null) {
  // Type 'string | null | undefined' is not assignable to type 'string'.
  // Type 'undefined' is not assignable to type 'string'. 
  const onlyString: string = maybeString; // Error
  const ignoreUndefinedAndNull: string = maybeString!; // Ok
}

2、可选链"?."

遇到null或者undefined时可以立即停止表达式的运行
const val = a?.b;
// 编译之后
var val = a === null || a === void 0 ? void 0 : a.b;

如果a为null或者void 0,会直接返回void 0,而不会接着执行a.b;
因此,以后我们可以不需要编写形如

if( a && a.b){
}

这样的保护性代码。更好的写法是:

if( a?.b){
}

唯一需要注意的是,?. 与 && 运算符并不100%等价。&& 专门用于检测 falsy 值,比如空字符串、0、NaN、null 和 false 等。而 ?. 只会验证对象是否为 null 或 undefined,对于 0 或空字符串来说,并不会出现 “短路”;

3、空值合并运算符(??)

  • 空值合并运算符(??)是一个逻辑运算符。当左侧操作数为 null 或 undefined 时,其返回右侧的操作数。否则返回左侧的操作数。
  • 与逻辑或(||)操作符不同,逻辑或会在左操作数为 falsy 值时返回右侧操作数。也就是说,如果你使用 || 来为某些变量设置默认的值时,你可能会遇到意料之外的行为。比如为 falsy 值(’’、NaN 或 0)时。
const foo = null ?? 'default string';
console.log(foo); // 输出:"default string"

const baz = 0 ?? 42;
console.log(baz); // 输出:0

以上 TS 代码经过编译后,会生成以下 ES5 代码:

"use strict";
var _a, _b;
var foo = (_a = null) !== null && _a !== void0 ? _a : 'default string';
console.log(foo); // 输出:"default string"

var baz = (_b = 0) !== null && _b !== void0 ? _b : 42;
console.log(baz); // 输出:0

三、函数详解

1、函数的可选类型

type SubtractsFnType = (num1: number, num2?: number) => number

const subtracts: SubtractsFnType = (a1: number, a2?: number) => {
    if (a2) {
        return a1 - a2
    } else {
        return a1

    }

}
console.log(subtracts(5));
console.log(subtracts(5, 1));

export{}

2、函数的默认值

// 必传参数 - 有默认值的参数 - 可选参数
function foo(y: number, x: number = 20) {
  console.log(x, y)
}

foo(30)
export{}

3、函数的剩余参数


function sum(initalNum: number, ...nums: number[]) {
  let total = initalNum
  for (const num of nums) {
    total += num
  }
  return total
}

console.log(sum(20, 30))
console.log(sum(20, 30, 40))
console.log(sum(20, 30, 40, 50))

4、this相关

  • this的默认推导
// this是可以被推导出来 info对象(TypeScript推导出来)
const info = {
  name: "why",
  eating() {
    console.log(this.name + " eating")
  }
}

info.eating()

export {}
  • this的不明确类型
type ThisType = { name: string };

function eating(this: ThisType, message: string) {
  console.log(this.name + " eating", message);
}

const info = {
  name: "why",
  eating: eating,
};

// 隐式绑定 info
info.eating("哈哈哈");

// 显示绑定
eating.call({name: "kobe"}, "呵呵呵")
eating.apply({name: "james"}, ["嘿嘿嘿"])

5、函数重载

在TypeScript中,如果我们编写了一个add函数,希望可以对字符串和数字类型进行相加,应该如何编写呢?

  1. 通过类型缩小
function add(a1: number | string, a2: number | string) {
  if (typeof a1 === "number" && typeof a2 === "number") {
    return a1 + a2
  } else if (typeof a1 === "string" && typeof a2 === "string") {
    return a1 + a2
  }
  // return a1 + a2;
}
add(10, 20)
  1. 通过函数重载
// 函数的重载: 函数的名称相同, 但是参数不同的几个函数, 就是函数的重载

/* 1.函数类型的声明 */
function add(num1: number, num2: number): number; // 没函数体
function add(num1: string, num2: string): string;

/* 2.函数体的实现  不能直接被调用  必须符合上面声明的类型才行 */
function add(num1: any, num2: any): any {
  if (typeof num1 === "string" && typeof num2 === "string") {
    return num1.length + num2.length;
  }
  return num1 + num2;
}
const result = add(20, 30);
const result2 = add("abc", "cba");
console.log(result);
console.log(result2);

// 必须和重载函数匹配 不然会报错
// add({name: "why"}, {age: 18})

export {};

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: "鸭子类型" 是一种编程概念,在 TypeScript 中也适用。它指的是一种类型的判定方式,是看它是否具有某些必要的属性或方法,而不是看它是否属于某个具体的类。 举个例子,假设我们有一个类型 `Duck`,它有一个方法 `quack()`。如果某个对象有一个方法 `quack()`,我们就可以认为它是一只鸭子,即使它并不是 `Duck` 类型的实例。 在 TypeScript 中,我们可以使用鸭子类型来实现非常灵活的类型检查,尤其是在使用第三方库或模块时,往往无法了解它们的所有类型信息。使用鸭子类型,我们可以只关注它们是否满足我们需要的接口即可。 ### 回答2: 鸭子类型是一种类型检测的方法,常见于动态编程语言如TypeScript中。它基于"当走起来像鸭子、叫起来像鸭子,那就是鸭子"的原则。 在鸭子类型中,对象的类型并不重要,而是关注对象是否具备特定的方法或属性。如果一个对象具有与目标类型所需的方法和属性相匹配,那么它就被视为具有该目标类型。 这种类型检测方法更加灵活,它不要求对象从特定类继承,而是关注对象的行为。因此,它允许我们在不创建类层级关系的情况下,共享方法和属性。 举个例子,有一个函数需要接受一个拥有`walk`方法的对象作为参数。如果我们使用鸭子类型,我们不需要验证该对象是否属于某个特定类,只需要验证它是否具有`walk`方法。只要对象具备`walk`方法,我们就可以将其传递给该函数,无需关心对象的类别。 总而言之,鸭子类型类型检测方法聚焦于对象的行为,而不是它的类别或继承关系。这种方式使得代码更加灵活、可复用,同时也鼓励了面向接口编程的思想。 ### 回答3: 在TS中,鸭子类型是一种动态类型的概念。它是一种基于对象的类型检测方式,与传统的基于类的继承方式不同。鸭子类型的核心思想是:只要对象具备某些特定的方法或属性,它就可以被当作该类型的实例。 鸭子类型来源于一句格言:“如果一只鸟看起来像鸭子、走路像鸭子、游泳像鸭子,那么它就是一只鸭子。”在编程中,这种思想被用于类型检测。也就是说,我们不需要显式地指定一个变量的类型,只需要检查它是否具备我们需要的方法或属性。 例如,我们定义了一个函数,需要传入一个具备`quack`方法的对象,用于发出声音。这个对象可以是一个鸭子的实例,也可以是一个看起来像鸭子的对象。在鸭子类型中,我们不必关心这个对象是否来自特定的类,只需要确保它具备`quack`方法即可。 以这种方式进行类型检测的好处是,我们可以更灵活地使用对象,不受限于特定的类或继承关系。这使得TS具备了动态语言的一些灵活性,同时又具备了类型检测的安全性。 需要注意的是,鸭子类型也有一些缺点。首先,它只能检测对象的方法和属性,而不关心类型的其他特性。其次,如果一个函数的参数是通过鸭子类型检测的,那么文档中对参数的类型要求将不再明确,可能导致使用者的困惑。 总而言之,鸭子类型是一种基于对象的动态类型检测方式,在TS中允许我们通过检查对象的方法和属性来判断其是否为某一类型的实例。它使得我们可以更灵活地使用对象,但也需要在一定程度上权衡灵活性和明确性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值