type ShapeKind ='rect'|'circle';
let foo: string = getSomeString();
let shape: ShapeKind ='rect';// disallowed because string isnot subset of ShapeKind
shape = foo;// allowed because ShapeKind is subset of string
foo = shape;
function foo(x: string | number){
if (typeof x ==='string'){// x's type is narrowed to string, so .length is valid console.log(x.length); // assignment respects declaration type, not narrowed type x = 1; console.log(x.length); // disallowed because x is now number } else { ... }}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
#3 使用可区分的联合类型而不是可选字段
当定义一组多态类型(如Shape)时,很容易这样开始写代码:
type Shape ={
kind:'circle'|'rect';
radius?: number;
width?: number;
height?: number;}
function getArea(shape: Shape){
return shape.kind==='circle'?
Math.PI* shape.radius!**2: shape.width!* shape.height!;}
type Circle ={ kind:'circle'; radius: number };
type Rect ={ kind:'rect'; width: number; height: number };
type Shape = Circle | Rect;
function getArea(shape: Shape){
return shape.kind==='circle'?
Math.PI* shape.radius**2: shape.width* shape.height;}
1.
2.
3.
4.
5.
6.
7.
8.
9.
从以上代码可以看出,类型收窄消除了强制类型转换的需要。
#4 使用类型谓词避免类型断言
如果你以正确的方式使用TypeScript的话,你会发现自己很少使用显式类型断言(比如value as SomeType);但是,有时你可能会冲动地写出诸如这样的代码:
type Circle ={ kind:'circle'; radius: number };
type Rect ={ kind:'rect'; width: number; height: number };
type Shape = Circle | Rect;
function isCircle(shape: Shape){
return shape.kind==='circle';}
function isRect(shape: Shape){
return shape.kind==='rect';}
const myShapes: Shape[]= getShapes();// error because typescript doesn't know the filtering// narrows typingconst circles: Circle[] = myShapes.filter(isCircle);// you may be inclined to add an assertion:// const circles = myShapes.filter(isCircle) as Circle[];
function isCircle(shape: Shape): shape is Circle {
return shape.kind==='circle';}
function isRect(shape: Shape): shape is Rect {
return shape.kind==='rect';}
...
// now you get Circle[] type inferred correctly
const circles = myShapes.filter(isCircle);
function getArea(shape: Shape){
switch (shape.kind){
case 'circle':
return Math.PI* shape.radius**2;
case 'rect':
return shape.width* shape.height;
default:
throw new Error('Unknown shape kind');}}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
通过使用never类型,静态类型检查就可以更早地查找到错误:
function getArea(shape: Shape){
switch (shape.kind){
case 'circle':
return Math.PI* shape.radius**2;
case 'rect':
return shape.width* shape.height;
default:// you'll get a type-checking error below // if any shape.kind is not handled above const _exhaustiveCheck: never = shape; throw new Error('Unknown shape kind'); }}
let foo ={ name:'foo'};// typed:{ name: string }
let Bar ={ name:'bar'}as const;// typed:{ name:'bar'}
let a =[1,2];// typed: number[]
let b =[1,2]as const;// typed:[1,2]// typed { kind:'circle; radius: number }let circle = { kind: 'circle' as const, radius: 1.0 };// the following won't work if circle wasn't initialized// with the const keywordlet shape: { kind: 'circle' | 'rect' } = circle;
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
使用satisfies来检查类型,而不影响推断的类型
请看以下示例:
type NamedCircle ={
radius: number;
name?: string;};
const circle: NamedCircle ={ radius:1.0, name:'yeah'};// error because circle.name can be undefined
console.log(circle.name.length);
// gets the unwrapped type out of a Promise;// idempotent if T isnot Promise
type ResolvedPromise<T>= T extends Promise<infer U>? U : T;
type t = ResolvedPromise<Promise<string>>;// t: string
// gets the flattened type of array T;// idempotent if T isnot array
type Flatten<T>= T extends Array<infer E>? Flatten<E>: T;
type e = Flatten<number[][]>;// e: number
type User ={
age: number;
gender: string;
country: string;
city: string
};
type Demographic ={ age: number: gender: string;};
type Geo ={ country: string; city: string;};
1.
2.
3.
4.
5.
6.
7.
8.
还不如使用pick实用程序提取新类型:
type User ={
age: number;
gender: string;
country: string;
city: string
};
type Demographic = Pick<User,'age'|'gender'>;
type Geo = Pick<User,'country'|'city'>;
1.
2.
3.
4.
5.
6.
7.
8.
与其复制函数的返回类型:
function createCircle(){
return {
kind:'circle'as const,
radius:1.0}}
function transformCircle(circle:{ kind:'circle'; radius: number }){
...
}
transformCircle(createCircle());
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
还不如使用ReturnType<T>提取:
function createCircle(){
return {
kind:'circle'as const,
radius:1.0}}
function transformCircle(circle: ReturnType<typeof createCircle>){
...
}
transformCircle(createCircle());
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
与其并行同步两种类型的shape(此处为config类型和Factory):
type ContentTypes ='news'|'blog'|'video';// config for indicating what content types are enabled
const config ={ news:true, blog:true, video:false}
satisfies Record<ContentTypes,boolean>;// factory for creating contents
type Factory ={
createNews:()=> Content;
createBlog:()=> Content;};
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
还不如使用映射类型和模板字面量类型根据config的形状自动推断正确的factory类型:
type ContentTypes ='news'|'blog'|'video';// generic factory type with a inferred list of methods
// based on the shape of the given Config
type ContentFactory<Config extends Record<ContentTypes,boolean>>={[k in string & keyof Config as Config[k] extends true? `create${Capitalize<k>}`
: never]:()=> Content;};// config for indicating what content types are enabled
const config ={ news:true, blog:true, video:false}
satisfies Record<ContentTypes,boolean>;
type Factory = ContentFactory<typeof config>;// Factory:{// createNews:()=> Content;// createBlog:()=> Content;//}