这是我参与11月更文挑战的第9天,活动详情查看:2021最后一次更文挑战
资料:递归的对象
我们来看一段代码:
let foo:any = {};
let bar = {
foo
}
foo.bar=bar;
将上述代码在chrome控制台打印,如下图所示,我们会发现它是一个递归的对象,即能够无线地 foo.bar.foo.bar......
当我们在编码的过程中遇到类似场景,我们期望的体验是编辑器能够一直有代码提示,但是实际上我们输入 foo. 之后 VSCode 并没有给出提示。
当遇到这类场景的时候,我们就需要用到 interface 来声明递归结构。如下:
interface Foo {
bar: {
foo: Foo
}
}
let foo: Foo;
let bar = {
foo
}
foo.bar = bar;
这个时候 VSCode 就会按照预期进行正确的代码提示,如下图:
资料:object vs Object vs {}
以下代码编译结论于 TypeScript 严格模式下得出
// tsconfig.json
{
"compilerOptions": {
"strictNullChecks": true, // strictNullChecks 需要设置为 true
...
}
...
}
首先我们来了解一下 TypeScript 中的 object 类型。
在 TypeScript 2.2 版本之前没有一个类型用于表述「非原始数据类型」,即一个值非 number、string、boolean、symbol、null 或 undefined,所以在 2.2 版本的 TypeScript 中加入了 object 类型(注意是首字母小写的 object, 而不是 Object) 那么新加入的 object 与 Object 或 {} 有什么区别呢? 带着这个疑问,我们通过官方示例代码来了解 object 的用法
declare function create(o: object | null): void;
create({ prop: 0 }); // OK
create(null); // OK
create(42); // Error
create("string"); // Error
create(false); // Error
create(undefined); // Error
从上述代码就能比较直观地感受到 object 类型「非原始数据类型」的特性。
Object VS object
基于上述代码我们用 Object 类型来做一个对比:
declare function create1(o: object): void;
declare function create2(o: Object): void;
create1({ prop: 0 }); // OK
create1(null); // Error
create1(undefined); // Error
create1(42); // Error
create1("string"); // Error
create1(false); // Error
create2({ prop: 0 }); // OK
create2(null); // Error
create2(undefined); // Error
create2(42); // Ok
create2("string"); // Ok
create2(false); // Ok
从上述代码我们就能直观地感受 Object 与 object 之间的区别,即 Object 可以被推论为 number、string、boolean 这三个原始数据类型。
Object VS any (非严格模式)
由于所有类型都继承自 Object ,所以我们有时候可能会困惑于以下两种变量声明方式:
let x: any;
let y: Object;
x = "hello word";
x = 123;
x = null;
x = {};
x = undefined;
x = function() {};
y = "hello word";
y = 123;
y = null; // 为了进行对比,此处是非严格模式,严格模式下无法通过
y = {};
y = undefined; // 为了进行对比,此处是非严格模式,严格模式下无法通过
y = function() {};
上述代码能够通过编译,但是实际上它们之间是存在区别的,any 没有接口(interface)而 Object 有 Object interface,所以 any 比 Object 更加自由,我们来看下面一段代码:
let a: any;
let b: Object;
a.doSomething(); // OK, 编译器会信任你的用法
b.doSomething(); // Error , b 中未声明 doSomething 方法
Object VS {}
{} 继承于 Object(所有类型都继承自 Object),但 Object 与 {} 在 TypeScript 中的用法是等价的。
类型别名
与 interface 类似的概念,类型别名。
type Person = {
name: string;
gender: string;
friend: { // 对象结构
name: string;
age: number;
}
}
// 这样就可以正确描述 alex 类型了
let alex: Person = {
name: 'Alex',
gender: 'male',
friend: {
name: 'Lucy',
age: 18,
// gender: 'female' // 报错
}
}
类型别名和 interface 到底有什么区别呢?
- 类型别名可以是一个联合类型,而 interface 不行
- 在面向对象的实践中,我们主要也是使用 interface
type Person = string | number;
习题:关于类型别名,下面用法错误的是?
// A
type Str = string;
const name: Str = 'panda';
// B
type Num = number;
const weight: Num = 10;
// C
type Obj = {
name: string,
weight: number
};
const animal: Obj = {
name: 'panda',
weight: 10
}
// D
type Obj = string | number;
const animal: Obj = {
name: 'panda',
weight: 10
}
答案:D
解析
类型别名使得我们可以对一个比较复杂的类型重命名,从而提高代码阅读性。
- A - name 被赋值
panda,所以声明为string是正确的。 - B - weight 被赋值
10,所以声明为number是正确的。 - C - 变量
animal被赋值为一个对象,本选项将animal声明为对象类型,且和赋值对象的结构类型相符。故正确。 - D - 变量
animal被赋值为一个对象,但本选项声明的联合类型中均为基本数据类型。故错误。