第二章 TypeScript3的新特性

  • Tuples
  • The unknown type
  • Project references
  • Default JSX properties

Tuples

元组类型用中括号表示,

1
2
3
let product: [string, number];

product = ["Table", 500];

JavaScript 的rest扩展语法

在JavaScript中,一个rest参数就是汇集多个参数以一个参数表示。因此它称为rest(剩余的、)。不要跟RESTful混淆。

TS中用三个点表示这种rest参数,

1
2
3
function logScores(...scores) {
console.log(scores);
}

Open-ended tuples

结合rest的扩展,tuple可以写成,

1
2
type Scores = [string, ...number[]];
const billyScores: Scores = ["Billy", 60, 70, 75];

Tuple function parameters

除了rest扩展写法,TS3支持带上参数类型,

1
2
3
function logScores(...scores: [...number[]]) {
console.log(scores);
}

或者,

1
2
3
4
5
6
type Scores = [string, ...number[]];

function logNameAndScores(...scores: Scores) {
console.log(scores);
}
logNameAndScores("Sally", 60, 70, 75, 70);

Spread expressions

TS3中允许使用扩展语法,

1
2
3
4
5
6
7
function logScore(score1: number, score2: number, score3: number) {
console.log(score1, score2, score3);
}

const scores: [number, number, number] = [75, 65, 80];

logScore(...scores);

但不支持open-ended tuples,因此下面代码编译出错,

1
2
const scoresUnlimited: [...number[]] = [75, 65, 80];
logScore(...scoresUnlimited);

Empty tuples

TS3中可以定义空tuple,

对于类型别名,

1
type Empty = [];

声明该类型的一个变量,

1
const empty: Empty = [];

如果尝试给该类型指派一个非空值,会出现编译错误,

1
const notEmpty: Empty = ["Billy"];

空tuple可能没什么用,它的主要用处在于作为联合类型(union type)的一部分。

1
2
3
4
5
6
7
type Scores = [] | [number] | [number, number] | [number, number, number]

const benScores: Scores = [];
const samScores: Scores = [55];
const bobScores: Scores = [95, 75];
const jayneScores: Scores = [65, 50, 70];
const sarahScores: Scores = [95, 50, 75, 75]; // illegal

可选tuple元素

TS中用?表示一个可选元素,

1
type Scores = [number, number?, number?];

这样就可以创建带一个到三个元素的变量,

1
2
3
const samScores: Scores = [55];
const bobScores: Scores = [95, 75];
const jayneScores: Scores = [65, 50, 70];

严格来说,TypeScript是把类型看做是“真实的”,因此对应Type Safe的层次也是看做是类型安全的,不匹配的类型将发生编译错误,

1
2
3
const sarahScores: Scores = [95, 50, 75, 75];	// illegal

const benScores: Scores = []; // illegal

Unknown type

TS中对未知类型使用unknow表示,unknown类型通常和any类型交替地使用,编译器不会对这种类型进行检查,因此,下面写法是正确的,

1
2
3
4
5
6
7
8
9
10
function logScores(scores: any) {
console.log(scores.firstName);
console.log(scores.scores);
}

// call this function
logScores({
name: "Billy",
scores: [60, 70, 75]
});

但如果改为,

1
2
3
4
function logScoresBetter(socres: unknown) {
console.log(scores.firstName);
console.log(scores.scores);
}

立即获得一个编译器警告,带程序依然有效。

Type checking with a type redicate

顾名思义,就是用“谓语”对类型作检查,

1
2
3
4
5
const scoresCheck = (
scores: any
): scores is { name: string; scores: number[] } => {
return "name" in socres && "scores" in scores;
};

这里的scores参数带有类型谓语,scores is { name: string; scores: number[] },以确保它包含类型属性namescores

可以结合类型别名,提高可读性,

1
2
3
4
5
6
7
type Scores = { name: string; scores: number[] }

const scoresCheck = (
scores: any
): scores is Scores => {
return "name" in scores && "scores" in scores;
};

类型谓语属于type guard的一种方式,还有其它几种实现type guard的方式。

Type narrowing with a type assertion

类型收缩(type narrowing)类似于Java的强制转换,通过类型断言的方式,使用as关键实现,

1
2
3
4
5
6
7
8
9
type Scores = {
name: string;
scores: number[]
};

function logScores(scores: unknown) {
console.log((scores as scores).name);
console.log((scores as Scores).scores);
}

Project reference

(略) 参考网上tsconfig.json内容,

Default JSX properties

在TS3之前,我们必须设置默认的Props属性,并检查是否为null,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
interface IProps {
text: string;
delimiter?: string;
}
class SplitText extends Component<IProps> {
static defaultProps = {
delimiter: ","
};
render() {
const bits = this.props.text.split(this.props.delimiter!);
return (
<ul>
{bits.map((bit: string) => (
<li key={bit}>{bit}</li>
))}
</ul>
);
}
}
const App = () => (
<div>
<SplitText text="Fred,Jane,Bob" />
</div>
);
export default App;

TS3之后,可以不必要将delimiter属性设置为optional的,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
interface IProps {
text: string;
delimiter: string;
}
class SplitText extends Component<IProps> {
static defaultProps = {
delimiter: ","
};
render() {
const bits = this.props.text.split(this.props.delimiter);
return (
<ul>
{bits.map((bit: string) => (
<li key={bit}>{bit}</li>
))}
</ul>
);
}
}

问题

  1. 怎么调用drawPoint这个函数,
1
2
3
4
5
function drawPoint(x: number, y: number, z: number) {
...
}
const point: [number, number, number] = [100, 200, 300];
drawPoint(...point);
  1. 创建另外一个版本的drawPoint,参数用[number, number, number]实现,

  2. 在问题2中,让z是可选的。