TypeScript entry-advanced type, typescript entry type
Advanced Type
Crossover type
The cross type is to combine multiple types into a new type. The new type has members of these types, and all the features of these types are their complexes, like the Union of a set
Example:
function extend<T,U>(first: T, second: U): T&U { let result = <T & U>{}; for (let id in first) { (<any>result)[id] = first[id]; } for (let id in second) { if (!result.hasOwnProperty(id)) { (<any>result)[id] = second[id]; } } return result;}class Person { constructor(public name: string) { }}interface Loggable { log(): void;}class myLoggable implements Loggable { log() { console.log('qwe'); }}let jim = extend(new Person('qq'), new myLoggable());console.log(jim.name);jim.log();
In this example, jim has the name attribute in Person and the log () method in myLoggable.
Union type
Union type. It is not like that the cross type is a collection of multiple types. It indicates that it is one of these types, such as the intersection in the set, only features of multiple types can be called.
For example, to pass a parameter to a function, the parameter may be number or string.
function padLeft(value: string, padding: any) { if (typeof padding === "number") { return Array(padding + 1).join(" ") + value; } if (typeof padding === "string") { return padding + value; } throw new Error(`Expected string or number, got '${padding}'.`);}padLeft("Hello world", 4); // " Hello world"
There is a problem here. Defining padding as any indicates that we can pass any value to padding. This error is not reported by TypeScript and will only be reported during compilation.
To solve this problem, you can use the Union type and use vertical bars to separate each type, indicating that it is one of these types.
function padLeft(value: string, padding: string | number) { ........}let f = padLeft("Hello world", true); // error
If a value is of the Union type, only members of these types can be accessed.
interface Bird { fly(); layEggs();}interface Fish { swim(); layEggs();}function getSmallPet(): Fish | Bird { ...}let pet = getSmallPet();pet.layEggs(); // okaypet.swim(); // errors
The return type of getSmallPet is a union type, so pet can only access the member layEggs () of Bird and Fish ()
In the above example, we do not know the type of pet, so it is impossible to access which are not public members. If we know the type of pet, you can access all members of this type.
Type Protection and differentiated types
To solve the problem of determining the specific types mentioned above, type assertions (type conversion) need to be introduced)
let pet = getSmallPet();if ((<Fish>pet).swim) { (<Fish>pet).swim();} else { (<Bird>pet).fly();}
The problem is obvious. It is troublesome to convert the type of pet every time.
User-Defined Type Protection
Type Protection can solve the shortcomings of the above Type assertions every time. Type Protection is some expressions that check at runtime to ensure the type in a specific scope. To define a Type Protection, we just need to define a function, and its return value is a type asserted.
function isFish(pet: Fish | Bird): pet is Fish { return (<Fish>pet).swim !== undefined;}
Pet is Fish is a type assertion. An asserted is in the form of parameterName is Type. parameterName must be a parameter name from the current function signature.
// There is no problem with the call of 'swim 'and 'fly'. if (isFish (pet) {pet. swim () ;}else {pet. fly ();}
TypeScript not only knows that it is Fish in if, but also knows that it is Bird in else.
Typeof Type Protection
We can use the previous padLeft code to implement the type assertion.
function isNumber(x: any): x is number { return typeof x === "number";}function isString(x: any): x is string { return typeof x === "string";}function padLeft(value: string, padding: string | number) { if (isNumber(padding)) { return Array(padding + 1).join(" ") + value; } if (isString(padding)) { return padding + value; } throw new Error(`Expected string or number, got '${padding}'.`);}
If you want to write in this way, you need to write a function for each original type, which is troublesome. TypeScript will set "typeof v = typeofname" and "typeof v! = Typeofname "is treated as type protection, so you don't have to write a function for an original type. You can simply use typeof.
function padLeft(value: string, padding: string | number) { if (typeof padding === "number") { return Array(padding + 1).join(" ") + value; } if (typeof padding === "string") { return padding + value; } throw new Error(`Expected string or number, got '${padding}'.`);}
Instanceof Type Protection
Instanceof Type Protection is a way to refine types through Constructors
Interface Padder {getPaddingString (): string} class SpaceRepeatingPadder implements Padder {constructor (private numSpaces: number) {} getPaddingString () {return Array (this. numSpaces + 1 ). join ("") ;}} class StringPadder implements Padder {constructor (private value: string) {}getpaddingstring () {return this. value ;}} function getRandomPadder () {return Math. random () <0.5? New Round (4): new StringPadder ("");} // type: Round | StringPadderlet padder: Padder = getRandomPadder (); if (padder instanceof SpaceRepeatingPadder) {padder; // Type Details: 'spacerepeatingpadder'} if (padder instanceof StringPadder) {padder; // Type Details: 'stringpadder '}
Type alias
A type alias is an alias for a type and can be used for basic data types.
type Name = string;type NameResolver = () => string;type NameOrResolver = Name | NameResolver;function getName(n: NameOrResolver): Name { if (typeof n === 'string') { return n; } else { return n(); }}
Different from the interface, the type alias does not create a new type, but the type name has changed.
type Alias = { num: number }interface Interface { num: number;}declare function aliased(arg: Alias): Alias;declare function interfaced(arg: Interface): Interface;
In the above Code, The interfaced return value type is Interface, while the aliased return value type is the object literal
Another difference between the type alias and the interface is that the type alias cannot be extends or implements
The two differences between the type alias and the interface are also the same, that is, generic type can be used.
type Tree<T> = { value: T; left: Tree<T>; right: Tree<T>;}
String Literal type
The string literal type can work well with the union type, type protection, and type alias.
type Easing = "ease-in" | "ease-out" | "ease-in-out";class UIElement { animate(dx: number, dy: number, easing: Easing) { if (easing === "ease-in") { // ... } else if (easing === "ease-out") { } else if (easing === "ease-in-out") { } else { // error! should not pass null or undefined. } }}let button = new UIElement();button.animate(0, 0, "ease-in");button.animate(0, 0, "uneasy"); // error: "uneasy" is not allowed here
You can only select one of the three specified types for transfer. If you select another type, an error is returned.
Identifiable Federation
The string literal type, union type, type protection, and type alias can be merged to create an advanced mode called recognizable union.
First, define three interfaces to be joined. Each interface has a kind attribute, but the value is different. The king attribute can be used as an identifiable feature and identifier.
interface Square { kind: "square"; size: number;}interface Rectangle { kind: "rectangle"; width: number; height: number;}interface Circle { kind: "circle"; radius: number;}
Then combine them.
type Shape = Square | Rectangle | Circle;
Use identifiable Federation
function area(s: Shape) { switch (s.kind) { case "square": return s.size * s.size; case "rectangle": return s.height * s.width; case "circle": return Math.PI * s.radius ** 2; }}
Note: If you add a new type to the Shape, you must add the corresponding judgment under the switch.
References:
TypeScript: the superset of JavaScript