-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
TypeScript 之 Object Types #221
Comments
|
@btea 成组和分组感觉都差不多,哈哈 |
@mqyqingfeng 嗯,是的,意思好像差不多,不过成组比较少见,刚开始读起来总感觉不通畅,还以为写错了 |
@btea 难道是因为我以前 PPT 做得比较多的缘故吗?因为 PPT 和 keynote 里将多个元素组成一起,用的就是名为 "成组" 的功能 😂 |
@mqyqingfeng 哈哈,专业术语 |
interface Animal {
name: string;
}
interface Dog extends Animal {
breed: string;
}
interface NotOkay {
[x: number]: Animal;
// 'number' index type 'Animal' is not assignable to 'string' index type 'Dog'.
[x: string]: Dog;
} 大佬,上面这段代码,怎么改都是报错的呀? 请问如果是这种情况的话 这里的索引类型这个案例怎么才能正确呢? 改成 |
type Either2dOr3d = [number, number, number?];
function setCoordinate(coord: Either2dOr3d) {
const [x, y, z] = coord;
const z: number | undefined
console.log(`Provided coordinates had ${coord.length} dimensions`);
// (property) length: 2 | 3
} 我的 ts 版本中不需要写 会提示已经声明过了 然后鼠标放上去 会提示这段代码的 可能是版本的原因吧 大家看到测试过的 有没有相同的问题的呀? |
@Hurryliwei 我们看下这段代码相关的文字:
所以修改为不报错的方法是把数字索引的返回类型设置为字符索引返回类型的子类型,比如这样: interface Animal {
name: string;
}
interface Dog extends Animal {
breed: string;
}
// Error: indexing with a numeric string might get you a completely separate type of Animal!
interface NotOkay {
[x: number]: Dog;
// 'number' index type 'Animal' is not assignable to 'string' index type 'Dog'.
[x: string]: Animal;
} |
@Hurryliwei 非常抱歉,setCoordinate 这段我写错了,其实 |
也就是这句话:数字索引的返回类型一定要是字符索引返回类型的子类型 所以 dog 是 animal 的子类型 所以需要满足 number 返回类型对应子类型 而 string 返回类型对应父类型? 大佬 我悟了 我悟了 这一个小知识点 早上一直卡着我 我想 WX 加你好友问的 但是怕您不方便 感谢感谢 太感谢了 |
我是怕大家 不懂 这不是错误 大佬 您很棒 |
@Hurryliwei 没事,微信勇敢来加,参与讨论和指正的同学我都直接邀请入群 |
那我加了哦 就是怕问多了 您会烦 也害怕打扰您工作 |
读了2遍,我也懂了 |
@JararvisQ 课代表,催更了 |
你好我已经收到你的邮件了,我会在3个工作日内及时回复你~
|
邮件已收到,谢谢!
|
interface StringArray { const myArray: StringArray = getStringArray(); 是不是getStringArray 这块应该写一个数组 |
ReadonlyArray类型不能修改指的是不能使用类似数组push的一些方法吗?比如let x: readonly string[] = [];x=['1','2']; 直接这样些并没有报错 |
邮件已收到,谢谢!
|
ReadonlyArray类型,它与Array相似,只是把所有可变方法(push、pop等)去掉了,因此可以确保数组创建后再也不能被修改,我们也只能通过数组元素索引来访问只读数组元素,但是不能修改只读数组元素。 |
上面的是数组类型赋值给元组类型,所以报错; |
接口继承和交叉类型里面下面这句话是不是需要把交集改成并集呢 取得是 string 和 number 的交集 |
我觉得翻译成这样好理解一点: |
@jizai1125 这种描述似乎更加清晰 👍 |
前言
TypeScript 的官方文档早已更新,但我能找到的中文文档都还停留在比较老的版本。所以对其中新增以及修订较多的一些章节进行了翻译整理。
本篇整理自 TypeScript Handbook 中 「Object Types」 章节。
本文并不严格按照原文翻译,对部分内容也做了解释补充。
对象类型(Object types)
在 JavaScript 中,最基本的将数据成组和分发的方式就是通过对象。在 TypeScript 中,我们通过对象类型(object types)来描述对象。
对象类型可以是匿名的:
也可以使用接口进行定义:
或者通过类型别名:
属性修饰符(Property Modifiers)
对象类型中的每个属性可以说明它的类型、属性是否可选、属性是否只读等信息。
可选属性(Optional Properties)
我们可以在属性名后面加一个
?
标记表示这个属性是可选的:在这个例子中,
xPos
和yPos
就是可选属性。因为他们是可选的,所以上面所有的调用方式都是合法的。我们也可以尝试读取这些属性,但如果我们是在
strictNullChecks
模式下,TypeScript 会提示我们,属性值可能是undefined
。在 JavaScript 中,如果一个属性值没有被设置,我们获取会得到
undefined
。所以我们可以针对undefined
特殊处理一下:这种判断在 JavaScript 中很常见,以至于提供了专门的语法糖:
这里我们使用了解构语法以及为
xPos
和yPos
提供了默认值。现在xPos
和yPos
的值在paintShape
函数内部一定存在,但对于paintShape
的调用者来说,却是可选的。在对象解构语法中,
shape: Shape
表示的是把shape
的值赋值给局部变量Shape
。xPos: number
也是一样,会基于xPos
创建一个名为number
的变量。readonly
属性(readonly Properties)在 TypeScript 中,属性可以被标记为
readonly
,这不会改变任何运行时的行为,但在类型检查的时候,一个标记为readonly
的属性是不能被写入的。不过使用
readonly
并不意味着一个值就完全是不变的,亦或者说,内部的内容是不能变的。readonly
仅仅表明属性本身是不能被重新写入的。TypeScript 在检查两个类型是否兼容的时候,并不会考虑两个类型里的属性是否是
readonly
,这就意味着,readonly
的值是可以通过别名修改的。索引签名(Index Signatures)
有的时候,你不能提前知道一个类型里的所有属性的名字,但是你知道这些值的特征。
这种情况,你就可以用一个索引签名 (index signature) 来描述可能的值的类型,举个例子:
这样,我们就有了一个具有索引签名的接口
StringArray
,这个索引签名表示当一个StringArray
类型的值使用number
类型的值进行索引的时候,会返回一个string
类型的值。一个索引签名的属性类型必须是
string
或者是number
。虽然 TypeScript 可以同时支持
string
和number
类型,但数字索引的返回类型一定要是字符索引返回类型的子类型。这是因为当使用一个数字进行索引的时候,JavaScript 实际上把它转成了一个字符串。这就意味着使用数字 100 进行索引跟使用字符串 100 索引,是一样的。尽管字符串索引用来描述字典模式(dictionary pattern)非常的有效,但也会强制要求所有的属性要匹配索引签名的返回类型。这是因为一个声明类似于
obj.property
的字符串索引,跟obj["property"]
是一样的。在下面的例子中,name
的类型并不匹配字符串索引的类型,所以类型检查器会给出报错:然而,如果一个索引签名是属性类型的联合,那各种类型的属性就可以接受了:
最后,你也可以设置索引签名为
readonly
。因为索引签名是
readonly
,所以你无法设置myArray[2]
的值。属性继承(Extending Types)
有时我们需要一个比其他类型更具体的类型。举个例子,假设我们有一个
BasicAddress
类型用来描述在美国邮寄信件和包裹的所需字段。这在一些情况下已经满足了,但同一个地址的建筑往往还有不同的单元号,我们可以再写一个
AddressWithUnit
:这样写固然可以,但为了加一个字段,就是要完全的拷贝一遍。
我们可以改成继承
BasicAddress
的方式来实现:对接口使用
extends
关键字允许我们有效的从其他声明过的类型中拷贝成员,并且随意添加新成员。接口也可以继承多个类型:
交叉类型(Intersection Types)
TypeScript 也提供了名为交叉类型(Intersection types)的方法,用于合并已经存在的对象类型。
交叉类型的定义需要用到
&
操作符:这里,我们连结
Colorful
和Circle
产生了一个新的类型,新类型拥有Colorful
和Circle
的所有成员。接口继承与交叉类型(Interfalces vs Intersections)
这两种方式在合并类型上看起来很相似,但实际上还是有很大的不同。最原则性的不同就是在于冲突怎么处理,这也是你决定选择那种方式的主要原因。
使用继承的方式,如果重写类型会导致编译错误,但交叉类型不会:
虽然不会报错,那
color
属性的类型是什么呢,答案是never
,取得是string
和number
的交集。泛型对象类型(Generic Object Types)
让我们写这样一个
Box
类型,可以包含任何值:现在
content
属性的类型为any
,可以用,但容易导致翻车。我们也可以代替使用
unknown
,但这也意味着,如果我们已经知道了contents
的类型,我们需要做一些预防检查,或者用一个容易错误的类型断言。一个更加安全的做法是将
Box
根据contents
的类型拆分的更具体一些:但是这也意味着我们不得不创建不同的函数或者函数重载处理不同的类型:
这样写就太繁琐了。
所以我们可以创建一个泛型
Box
,它声明了一个类型参数 (type parameter):你可以这样理解:
Box
的Type
就是contents
拥有的类型Type
。当我们引用
Box
的时候,我们需要给予一个类型实参替换掉Type
:把
Box
想象成一个实际类型的模板,Type
就是一个占位符,可以被替代为具体的类型。当 TypeScript 看到Box<string>
,它就会替换为Box<Type>
的Type
为string
,最后的结果就会变成{ contents: string }
。换句话说,Box<string>
和StringBox
是一样的。不过现在的
Box
是可重复使用的,如果我们需要一个新的类型,我们完全不需要再重新声明一个类型。这也意味着我们可以利用泛型函数避免使用函数重载。
类型别名也是可以使用泛型的。比如:
使用别名对应就是:
类型别名不同于接口,可以描述的不止是对象类型,所以我们也可以用类型别名写一些其他种类的的泛型帮助类型。
Array
类型(The Array Type)我们之前讲过
Array
类型,当我们这样写类型number[]
或者string[]
的时候,其实它们只是Array<number>
和Array<string>
的简写形式而已。类似于上面的
Box
类型,Array
本身就是一个泛型:现代 JavaScript 也提供其他是泛型的数据结构,比如
Map<K, V>
,Set<T>
和Promise<T>
。因为Map
、Set
、Promise
的行为表现,它们可以跟任何类型搭配使用。ReadonlyArray
类型(The ReadonlyArray Type)ReadonlyArray
是一个特殊类型,它可以描述数组不能被改变。ReadonlyArray
主要是用来做意图声明。当我们看到一个函数返回ReadonlyArray
,就是在告诉我们不能去更改其中的内容,当我们看到一个函数支持传入ReadonlyArray
,这是在告诉我们我们可以放心的传入数组到函数中,而不用担心会改变数组的内容。
不像
Array
,ReadonlyArray
并不是一个我们可以用的构造器函数。然而,我们可以直接把一个常规数组赋值给
ReadonlyArray
。TypeScript 也针对
ReadonlyArray<Type>
提供了更简短的写法readonly Type[]
。最后有一点要注意,就是
Arrays
和ReadonlyArray
并不能双向的赋值:元组类型(Tuple Types)
元组类型是另外一种
Array
类型,当你明确知道数组包含多少个元素,并且每个位置元素的类型都明确知道的时候,就适合使用元组类型。在这个例子中,
StringNumberPair
就是string
和number
的元组类型。跟
ReadonlyArray
一样,它并不会在运行时产生影响,但是对 TypeScript 很有意义。因为对于类型系统,StringNumberPair
描述了一个数组,索引 0 的值的类型是string
,索引 1 的值的类型是number
。如果要获取元素数量之外的元素,TypeScript 会提示错误:
我们也可以使用 JavaScript 的数组解构语法解构元组:
除了长度检查,简单的元组类型跟声明了
length
属性和具体的索引属性的Array
是一样的。在元组类型中,你也可以写一个可选属性,但可选元素必须在最后面,而且也会影响类型的
length
。Tuples 也可以使用剩余元素语法,但必须是 array/tuple 类型:
有剩余元素的元组并不会设置
length
,因为它只知道在不同位置上的已知元素信息:可选元素和剩余元素的存在,使得 TypeScript 可以在参数列表里使用元组,就像这样:
基本等同于:
readonly
元组类型(readonly Tuple Types)元组类型也是可以设置
readonly
的:这样 TypeScript 就不会允许写入
readonly
元组的任何属性:在大部分的代码中,元组只是被创建,使用完后也不会被修改,所以尽可能的将元组设置为
readonly
是一个好习惯。如果我们给一个数组字面量
const
断言,也会被推断为readonly
元组类型。尽管
distanceFromOrigin
并没有更改传入的元素,但函数希望传入一个可变元组。因为point
的类型被推断为readonly [3, 4]
,它跟[number number]
并不兼容,所以 TypeScript 给了一个报错。TypeScript 系列
TypeScript 系列文章由官方文档翻译、重难点解析、实战技巧三个部分组成,涵盖入门、进阶、实战,旨在为你提供一个系统学习 TS 的教程,全系列预计 40 篇左右。点此浏览全系列文章,并建议顺便收藏站点。
微信:「mqyqingfeng」,加我进冴羽唯一的读者群。
如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎 star,对作者也是一种鼓励。
The text was updated successfully, but these errors were encountered: