interface SomeType {
readonly prop: string;
}
function doSomething(obj: SomeType) {
console.log(prop has the value '${obj.prop}'.
);
obj.prop = “hello”;
// 无法分配到 “prop” ,因为它是只读属性
}
不过使用readonly
并不意味着一个值就完全是不变的,亦或者说,内部的内容是不能变的。readonly
仅仅表明属性本身是不能被重新写入的。大家应该猜到了,如果是引用类型,则可以避开这个问题。
interface Developer {
readonly fe: { name: string; age: number };
}
function getDeveloper(developer: Developer) {
console.log(developer.fe.name);
// (property) name: string
developer.fe.name = ‘余光’;
developer.fe.age++;
}
function getDeveloper1(developer: Developer) {
console.log(developer.fe.name);
// (property) name: string
developer.fe = {
name: “余光”,
age: 18,
};
// 无法分配到 “fe” ,因为它是只读属性。ts(2540)
}
TypeScript 在检查两个类型是否兼容的时候,并不会考虑两个类型里的属性是否是readonly
,这就意味着,readonly
的值是可以通过别名修改的。
interface Person {
name: string;
age: number;
}
interface ReadonlyPerson {
readonly name: string;
readonly age: number;
}
let writablePerson: Person = {
name: “Person McPersonface”,
age: 42,
};
// works
let readonlyPerson: ReadonlyPerson = writablePerson;
console.log(readonlyPerson.age); // prints ‘42’
writablePerson.age++;
console.log(readonlyPerson.age); // prints ‘43’
2.3 索引签名(Index Signatures)
有的时候,你不能提前知道一个类型里的所有属性的名字,但是你知道这些值的特征。
这种情况,你就可以用一个索引签名 (index signature) 来描述可能的值的类型,举个例子:
interface StringArray {
}
const myArray: StringArray = [1, 2, 3];// ❌ 不能将类型“number”分配给类型“string”
const secondItem = myArray[1]; // const secondItem: string
这样,我们就有了一个具有索引签名的接口StringArray
,一个索引签名的属性类型必须是 string 或者是 number。
有时我们需要一个比其他类型更具体的类型。举个例子,假设我们有一个BasicGoods
类型用来描述一个商品的基本信息
interface BasicGoods {
color: string;
size: string;
brand: string;
address: string;
}
这在一些情况下已经满足了,但同一个品牌的商品可能在,不同的分类中,例如:
interface BasicGoodsWithCategory {
color: string;
size: string;
brand: string;
address: string;
category: string
}
这样写固然可以,但为了加一个字段,就是要完全的拷贝一遍。我们可以改成继承BasicGoods
的方式来实现:
interface BasicGoodsWithCategory extends BasicGoods{
category: string
}
对接口使用extends
关键字允许我们有效的从其他声明过的类型中拷贝成员,并且随意添加新成员。
接口也可以继承多个类型:
interface Colorful {
color: string;
}
interface Circle {
radius: number;
}
interface ColorfulCircle extends Colorful, Circle {}
const cc: ColorfulCircle = {
color: “red”,
radius: 42,
};
TypeScrip 也提供了名为交叉类型的方法,用于合并已经存在的对象类型。交叉类型的定义需要用到&
操作符:
interface Colorful {
color: string;
}
interface Circle {
radius: number;
}
type group = Colorful & Circle;
function draw(circle: group) {
console.log(Color was ${circle.color}
); // (property) Circle.radius: number
console.log(Radius was ${circle.radius}
); // (property) Circle.radius: number
}
这里,我们连结Colorful
和Circle
产生了一个新的类型,新类型拥有Colorful
和Circle
的所有成员。
组合和继承,在上面的两个例子中解决的问题是一样的,那么怎么区分呢?
五、接口继承与交叉类型(Interfalces vs Intersections)
这两种方式在合并类型上看起来很相似,但实际上还是有很大的不同。最原则性的不同就是在于冲突怎么处理,这也是你决定选择那种方式的主要原因。
interface Colorful {
color: string;
}
interface ColorfulSub extends Colorful {
color: number;
}
// ❌
// 接口“ColorfulSub”错误扩展接口“Colorful”。
// 属性“color”的类型不兼容。
// 不能将类型“number”分配给类型“string”。
使用继承的方式,如果重写类型会导致编译错误,但交叉类型不会:
interface Colorful {
color: string;
}
type ColorfulSub = Colorful & {
color: number;
};
虽然不会报错,那 color 属性的类型是什么呢,答案是never
,取得是string
和number
的交集。
六、泛型对象类型(Generic Object Types)
让我们写这样一个Box类型,可以包含任何值,此时需要做一些预防检查,或者用一个容易错误的类型断言。
interface Box {
contents: unknown;
}
let x: Box = {
contents: “hello world”,
};
// we could check ‘x.contents’
if (typeof x.contents === “string”) {
console.log(x.contents.toLowerCase());
}
// or we could use a type assertion
console.log((x.contents as string).toLowerCase());
一个更加安全的做法是将 Box 根据 contents 的类型拆分的更具体一些:
interface NumberBox {
contents: number;
}
interface StringBox {
contents: string;
}
interface BooleanBox {
contents: boolean;
}
但是这也意味着我们不得不创建不同的函数或者函数重载处理不同的类型:
function setContents(box: StringBox, newContents: string): void;
function setContents(box: NumberBox, newContents: number): void;
function setContents(box: BooleanBox, newContents: boolean): void;
function setContents(box: { contents: any }, newContents: any) {
box.contents = newContents;
}
这样写就太繁琐了。此时引入一个概念——泛型,反省Box
,它声明了一个类型参数 (type parameter):
interface Box {
contents: Type;
}
你可以这样理解:Box的Type
就是contents
拥有的类型Type
。当我们引用Box
的时候,我们需要给予一个类型实参替换掉Type
:
let aaa: Box = {
contents: 1
};
// ❌ 不能将类型“number”分配给类型“string”。
把 Box 想象成一个实际类型的模板,Type 就是一个占位符,可以被替代为具体的类型。当 TypeScript 看到 Box<string>
,它就会替换为 Box<Type>
的 Type
为 string
,最后的结果就会变成 { contents: string }
。换句话说,Box<string>
和 StringBox
是一样的。
interface Box {
contents: Type;
}
interface StringBox {
contents: string;
}
不过现在的 Box 是可重复使用的,如果我们需要一个新的类型,我们完全不需要再重新声明一个类型。
interface Box {
contents: Type;
}
interface Apple {
// …
}
// Same as ‘{ contents: Apple }’.
type AppleBox = Box;
// 这也意味着我们可以利用泛型函数避免使用函数重载。
function setContents(box: Box, newContents: Type) {
box.contents = newContents;
}
6.1 类型别名与泛型
interface Box {
contents: Type;
}
使用别名对应就是:
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)
最后
基础知识是前端一面必问的,如果你在基础知识这一块翻车了,就算你框架玩的再6,webpack、git、node学习的再好也无济于事,因为对方就不会再给你展示的机会,千万不要因为基础错过了自己心怡的公司。前端的基础知识杂且多,并不是理解就ok了,有些是真的要去记。当然了我们是牛x的前端工程师,每天像背英语单词一样去背知识点就没必要了,只要平时工作中多注意总结,面试前端刷下题目就可以了。
什么?你问面试题资料在哪里,这不是就在你眼前吗(滑稽
**
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-cJDJysor-1713614390337)]
[外链图片转存中…(img-Xubz1oxu-1713614390337)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
[外链图片转存中…(img-Ic2914Bn-1713614390338)]
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)
[外链图片转存中…(img-OnvhRy6g-1713614390338)]
最后
基础知识是前端一面必问的,如果你在基础知识这一块翻车了,就算你框架玩的再6,webpack、git、node学习的再好也无济于事,因为对方就不会再给你展示的机会,千万不要因为基础错过了自己心怡的公司。前端的基础知识杂且多,并不是理解就ok了,有些是真的要去记。当然了我们是牛x的前端工程师,每天像背英语单词一样去背知识点就没必要了,只要平时工作中多注意总结,面试前端刷下题目就可以了。
什么?你问面试题资料在哪里,这不是就在你眼前吗(滑稽