TS学习笔记
快速浏览
ts可以自动识别js的变量类型
const message: string = "Hello, TypeScript!";
console.log("variable type is meaaage: " + message);
使用interface定义一个接口,可以显示的声明变量类型
interface Person {name: string;age: number;
}const person: Person = {name: "John",age: 30
};console.log("interface :" + person.name);
interface与class之间的联合使用 (不理解为什么要这要用)
注:这里的constructor是类的构造器
interface User{name: string;age: number;
}class UserAccount {name: string;age: number;constructor(name: string, age: number){this.name = name;this.age = age;}
}const user: User = new UserAccount("Tom", 25);
console.log("interface and class :"+user.name);
接口的一些作用
interface Shape{height: number;width: number;
}// 1. 规定返回类型
function xxx(): Shape{const shape : Shape = {height: 10,width: 20}return shape;}//2.参数类型的约束
function aaa(shape: Shape){//...
}
联合
组合类型
联合 (这些变量只能取其中的一个)
type myBool = true | false;
type myString = "hello" | "the world";
联合在函数参数中的使用
该函数将参数统一封装成数组对象
function wrapInArray(obj: string | string[]): any[]{if(typeof obj === "string") return [obj];return obj;
}
要了解变量的类型, 使用 typeof
:
类型 | 推断语句 |
---|---|
string | typeof s === "string" |
number | typeof n === "number" |
boolean | typeof b === "boolean" |
undefined | typeof undefined === "undefined" |
function | typeof f === "function" |
array | Array.isArray(a) |
泛型
- 什么是泛型
泛型就是:给类型加上“变量”,这样函数、类或接口就可以在不固定具体类型的情况下复用。
换句话说,泛型 = 类型的参数化。
比如普通函数:
function identity(arg: number): number { return arg; }
只能处理 number
类型。
如果我想让它既能处理 number
,也能处理 string
,也能处理 boolean
?
这时就需要 泛型。
- 基本语法
function identity<T>(arg: T): T { return arg;
}
T
就是泛型参数(相当于“类型变量”)。- 调用时可以指定类型:
identity<string>("hello"); // 返回 "hello" identity<number>(42); // 返回 42
- 也可以让 TypeScript 自动推断:
identity("hello"); // 推断 T 为 string
identity(42); // 推断 T 为 number
- 泛型的常见用法
(1) 泛型函数
function wrapInArray<T>(value: T): T[] { return [value];
} wrapInArray("hi"); // 类型: string[]
wrapInArray(100); // 类型: number[]
(2) 泛型接口
interface Box<T> { content: T; }
let stringBox: Box<string> = { content: "hello" };
let numberBox: Box<number> = { content: 123 };
(3) 泛型类
class DataStore<T> { private data: T[] = []; add(item: T) { this.data.push(item); } getAll(): T[] { return this.data; }
}
const stringStore = new DataStore<string>();
stringStore.add("hi");
stringStore.add("world");
console.log(stringStore.getAll()); // ["hi", "world"]
(4) 泛型约束 (extends)
有时你不希望 T 随便是什么类型,可以加 约束:
注:这里的约束是,对象必须要有length属性
interface HasLength { length: number; }
function logLength<T extends HasLength>(item: T): void { console.log(item.length); } logLength("hello"); // ✅ string 有 length
logLength([1, 2, 3]); // ✅ array 有 length// logLength(123); // ❌ number 没有 length
(5) 多个泛型参数
function pair<K, V>(key: K, value: V): [K, V] { return [key, value]; }
let result = pair("id", 123); // 类型: [string, number]
- 泛型的好处
-
复用性强:不用为每个类型都写一个函数/类
-
类型安全:比
any
更安全,保留了类型信息 -
自动推断:减少手写类型的负担
✅ 总结一句话:
泛型就是类型的参数化,让代码既灵活又安全。
结构化的类型系统(structural type system)
TypeScript 的一个核心原则是类型检查基于对象的属性和行为(type checking focuses on the shape that values have)。这有时被叫做“鸭子类型”或“结构类型”(structural typing)。
在结构化的类型系统当中,如果两个对象具有相同的结构,则认为它们是相同类型的。
interface Point { x: number; y: number; }
function logPoint(p: Point) { console.log(`${p.x}, ${p.y}`);
} // 打印 "12, 26" const point = { x: 12, y: 26 };
logPoint(point);
point
变量从未声明为 Point
类型。 但是,在类型检查中,TypeScript 将 point
的结构与 Point
的结构进行比较。它们的结构相同,所以代码通过了。
结构匹配只需要匹配对象字段的子集。
const point3 = { x: 12, y: 26, z: 89 };
logPoint(point3); // 打印 "12, 26" const rect = { x: 33, y: 3, width: 30, height: 80 };
logPoint(rect); // 打印 "33, 3" const color = { hex: "#187ABF" };
logPoint(color); //这里会报错
//Argument of type '{ hex: string; }' is not assignable to parameter of type 'Point'. Type '{ hex: string; }' is missing the following properties from type 'Point': x, y
类和对象确定结构的方式没有区别:
interface Point { x: number; y: number; }
function logPoint(p: Point) { console.log(`${p.x}, ${p.y}`); } // ---分割线--- class VirtualPoint { x: number; y: number; constructor(x: number, y: number) { this.x = x; this.y = y; }
}
const newVPoint = new VirtualPoint(13, 56);
logPoint(newVPoint); // 打印 "13, 56"
如果对象或类具有所有必需的属性,则 TypeScript 将表示是它们匹配的,而不关注其实现细节。
手册阅读
基础
静态类型检查
静态类型系统描述了程序运行时值的结构和行为。像 TypeScript 这样的静态类型检查器会利用类型系统提供的信息,并在事态发展不对劲的时候告知我们。
图中的上面是js,并不会报错;下面是ts,会显示错误。
非异常失败
- 当没有属性的时候,js会返回undefined,而ts会直接报错
比如这里的错误,ts会显示没有该函数。
函数调用
这里函数调用的失败
function flipCoin() {
// 应该是 Math.random()
return Math.random < 0.5;// Operator '<' cannot be applied to types '() => number' and 'number'.
}
同时一些基本逻辑的错误,也会显示。
·
类型工具
TypeScript 可以在我们的代码出现错误时捕获 bug。这很好,但更关键的是,它也能够在一开始就防止我们的代码出现错误。类型检查器可以通过获取的信息检查我们是否正在访问变量或者其它属性上的正确属性。一旦它获取到了这些信息,它也能够提示你可能想要访问的属性。这意味着 TypeScript 也能用于编辑代码。我们在编辑器中输入的时候,核心的类型检查器能够提供报错信息和代码补全。人们经常会谈到 TypeScript 在工具层面的作用,这就是一个典型的例子。
ts转换成js时
-
擦除类型(参数的类型注解不见)
-
降级(类似这样将更新或者“更高”版本的 ECMAScript 向下降级为更旧或者“更低”版本的代码,就是所谓的降级。)
-
严格性(开发者可以自己调节)
-
noImplicitAny
回想一下,在前面的某些例子中,TypeScript 没有为我们进行类型推断,这时候变量会采用最宽泛的类型:
any
。这并不是一件最糟糕的事情 —— 毕竟,使用any
类型基本就和纯 JavaScript 一样了。
但是,使用any
通常会和使用 TypeScript 的目的相违背。你的程序使用越多的类型,那么在验证和工具上你的收益就越多,这意味着在编码的时候你会遇到越少的 bug。启用 noImplicitAny 配置项,在遇到被隐式推断为any
类型的变量时就会抛出一个错误 -
strictNullChecks
默认情况下,
null
和undefined
可以被赋值给其它任意类型。这会让你的编码更加容易,但世界上无数多的 bug 正是由于忘记处理null
和undefined
导致的 —— 有时候它甚至会带来数十亿美元的损失!strictNullChecks 配置项让处理null
和undefined
的过程更加明显,让我们不用担心自己是否忘记处理null
和undefined
。
-
基本类型
any: 这个数据类型,可以向里面塞任何东西,或者当成函数调用,ts都不会报错。但在写代码的时候,避免使用这种数据类型,因为不安全。
注:当数据没有被指明数据类型时,并且编译器不能从上下文推断出变量的类型时,编译器会默认数据类型为any
函数:可以声明参数类型和返回值类型,同时使用箭头函数或者匿名函数时,参数的类型ts会利用上下文进行推断
对象
对象的属性如果没有声明其类型,默认值也是any
属性可以是全部,也可以是部分,只需要一个?
如果有属性没有传递,那么该属性的是 undefined
的状态
function printName(obj: {first:string,last?:string}){//...if(obj.last !== undefined){console.log(obj.latt.toUpperCase());}// A safe alternative using modern JavaScript syntax:console.log(obj.last?.toUpperCase());
}printName({first:"John",last:"Doe"});
printName({first:"Jane"});
Union Type
Defining a Union Type
The first way to combine types you might see is a union type. A union type is a type formed from two or more other types, representing values that may be any one of those types. We refer to each of these types as the union’s members.
Let’s write a function that can operate on strings or numbers:
function printID(id: number | string){console.log(`ID is ${id}`);
}printID(123);
printID("456");
if the id type is not number or string,it will throw an error.
注:
- TypeScript will only allow you to do things with the union if that thing is valid for every member of the union. For example, if you have the union
string | number
, you can’t use methods that are only available onstring
: --> 变量调用的方法必须满足联合体里面的所有类型
解决方案:Narrowing 联合体,例子如下
function printId(id: number | string) {if (typeof id === "string") {// In this branch, id is of type 'string'console.log(id.toUpperCase());} else {// Here, id is of type 'number'console.log(id);}
}
- 如果联合体成员的所有成员类型都有这个方法,就不需要缩小narrowing联合体
重命名
- 在下面这个示例中,我们将一个对象取名为Point了
type Point = {x: number;y: number;
}function drawPoint(point: Point){console.log(`(${point.x},${point.y})`);
}
drawPoint({x:10,y:20});
- 类型别名可以为任何类型重命名,比如联合体
type ID = number | string;
- type 这个关键字z在TypeScript 中不会产生新类型,只是原类型的别名
接口
接口声明 是命名对象类型的另一种方式:
interface Point { x: number; y: number; } function printCoord(pt: Point) { console.log("The coordinate's x value is " + pt.x); console.log("The coordinate's y value is " + pt.y);
} printCoord({ x: 100, y: 100 });
就像我们上面使用类型别名时一样,这个示例的工作方式就像我们使用了匿名对象类型一样。 TypeScript 只关心我们传递给 printCoord
的值的结构 - 它只关心它是否具有预期的属性。 只关心类型的结构和功能,这就是为什么我们说 TypeScript 是一个 结构化类型 的类型系统。
typed 和 接口之间的区别
类型别名和接口非常相似,在大多数情况下你可以在它们之间自由选择。 几乎所有的 interface
功能都可以在 type
中使用,关键区别在于不能重新开放类型以添加新的属性,而接口始终是可扩展的。
![[Pasted image 20251004174033.png]]
Type Assertions
Type Assertions(类型断言) = 告诉 TypeScript 编译器一个值具体是什么类型。
let someValue: unknown = "Hello World";
let strLength: number = (someValue as string).length;
- 用法:
value as Type
- 常用于 DOM 操作、联合类型缩小、绕过编译器类型不确定时的报错。
- 不会改变运行时的值,只是编译器检查时的“强制声明”。
注:Like a type annotation, type assertions are removed by the compiler and won’t affect the runtime behavior of your code.
字面量
- 基本说明
let x: "hello" = "hello";
// OK
x = "hello";// ...
//Type '"howdy"' is not assignable to type '"hello"'.
x = "howdy";
- 字面量和联合体结合
function printText(s: string, alignment: "left" | "right" | "center") {// ...
}printText("Hello, world", "left");
printText("G'day, mate", "centre");
//Argument of type '"centre"' is not assignable to parameter of type '"left" | "right" | "center"'.
- 更多说明
interface Options {width: number;
}function configure(x: Options | "auto") {// ...
}configure({ width: 100 });
configure("auto");
configure("automatic");
//Argument of type '"automatic"' is not assignable to parameter of type 'Options | "auto"'.
- 示例说明用法,结合类型断言
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"'.
两种修改方式
- You can change the inference by adding a type assertion in either location:
// Change 1: const req = { url: "https://example.com", method: "GET" as "GET" }; // Change 2 handleRequest(req.url, req.method as "GET");
Change 1 means “I intend for req.method
to always have the literal type "GET"
”, preventing the possible assignment of "GUESS"
to that field after. Change 2 means “I know for other reasons that req.method
has the value "GET"
“. —> 将 method
中进行一个类型断言,告诉编译器它的值是对的
- You can use
as const
to convert the entire object to be type literals:
const req = { url: "https://example.com", method: "GET" } as const;
handleRequest(req.url, req.method);
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会将每一个属性都变成const;req前面的const只是req的指向不能改变
null and undefined
strictNullChecks
off
With strictNullChecks
off, values that might be null
or undefined
can still be accessed normally, and the values null
and undefined
can be assigned to a property of any type. This is similar to how languages without null checks (e.g. C#, Java) behave. The lack of checking for these values tends to be a major source of bugs; we always recommend people turn strictNullChecks
on if it’s practical to do so in their codebase. —> 大多数的bug,都是由于它引起的,所以建议打开它
strictNullChecks
on
With strictNullChecks
on, when a value is null
or undefined
, you will need to test for those values before using methods or properties on that value. Just like checking for undefined
before using an optional property, we can use narrowing to check for values that might be null
: —> 可以使用缩小来判断null的发生,如果没有这个判断,编译器会报错
function doSomething(x: string | null) { if (x === null) { // do nothing } else { console.log("Hello, " + x.toUpperCase()); }
}
Non-null Assertion Operator (Postfix !
)
TypeScript also has a special syntax for removing null
and undefined
from a type without doing any explicit checking. Writing !
after any expression is effectively a type assertion that the value isn’t null
or undefined
:
function liveDangerously(x?: number | null) { // No error console.log(x!.toFixed());
}
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
. —> 告诉编译器,这个一定不是null 或者 undefined
JS补充内容
🔑 JavaScript 基本类型(原始类型)
-
boolean
- 布尔类型,只有两个值:
true
和false
。 - 例子:
let isLogin = true; let finished = false;
- 布尔类型,只有两个值:
-
number
- 数字类型,包括整数和浮点数(JS 中没有单独的整数类型)。
- 还包括一些特殊值:
NaN
(不是数字)、Infinity
(无穷大)、-Infinity
- 例子:
let age = 25; let price = 9.99; let notANumber = NaN; let max = Infinity;
-
**`bigint
- 表示任意精度的整数,可以处理超大数字。
- 以
n
结尾来表示:
let big = 123456789012345678901234567890n;
-
string
- 字符串类型,用于文本。
- 可以用
'单引号'
、"双引号"
或`反引号`
(模板字符串)。 - 例子:
let name = "Alice"; let greet = `Hello, ${name}!`;
-
symbol
- ES6 引入,表示一个唯一的值,常用于对象的属性键。
- 例子:
let id = Symbol("id"); let obj = { [id]: 123 };
-
null
- 表示“空值”,即有意让变量没有值。
- 通常用来表示某个值暂时不存在。
- 例子:
let user = null;
-
undefined
- 表示“未定义”,通常是变量声明了但还没有赋值时的默认值。
- 例子:
let x; console.log(x); // undefined