TypeScript
Why use types?
1. Enhance code quality and understandability. 2. It's better for the compiler to catch errors than to have things fail at runtime. 3. Types are one of the best forms of documentation we can have.
What are Ambients in TypeScripts and when to use them?
Ambients or Ambient declarations are a way of telling the TypeScript compiler that the actual source code exists elsewhere. Ambient declarations help to seamlessly integrate other js libraries into TypeScript. Ambient declarations are by convention kept in a type declaration file with d.ts extension. The syntax for declaring ambient variables or modules will be as following: declare module Module_Name { }
What's wrong with this? function fn(x: () => any) { x(); }
Because you aren't returning anything, you shouldn't use `any`. You should use `void`. Why: Using void is safer because it prevents you from accidently using the return value of x in an unchecked way: function fn(x: () => void) { var k = x(); // oops! meant to do something else k.doSomething(); // error, but would be OK if the return type had been 'any' }
How can you allow classes defined in a module to accessible outside of the module?
By using the `export` keyword: Classes define in a module are available within the module. Outside the module you can't access them. module Vehicle { class Car { constructor ( public make: string, public model: string) { } } var audiCar = new Car("Audi", "Q7"); } // This won't work var fordCar = Vehicle.Car("Ford", "Figo"); As per above code, fordCar variable will give us compile time error. To make classes accessible outside module use export keyword for classes. module Vehicle { export class Car { constructor ( public make: string, public model: string) { } } var audiCar = new Car("Audi", "Q7"); } // This works now var fordCar = Vehicle.Car("Ford", "Figo");
Explain generics
Generics are able to create a component or function to work over a variety of types rather than a single one. /** A class definition with a generic parameter */ class Queue<T> { private data = []; push = (item: T) => this.data.push(item); pop = (): T => this.data.shift(); } const queue = new Queue<number>(); queue.push(0); queue.push("1"); // ERROR : cannot push a string. Only numbers allowed
Consider the code: class Foo { save(callback: Function) : void { //Do the save var result : number = 42; //We get a number from the save operation //Can I at compile-time ensure the callback accepts a single parameter of type number somehow? callback(result); } } var foo = new Foo(); var callback = (result: string) : void => { alert(result); } foo.save(callback); Can you make the result parameter in save a type-safe function? Rewrite the code to demonstrate.
In TypeScript you can declare your callback type like: type NumberCallback = (n: number) => any; class Foo { // Equivalent save(callback: NumberCallback): void { console.log(1) callback(42); } } var numCallback: NumberCallback = (result: number) : void => { console.log("numCallback: ", result.toString()); } var foo = new Foo(); foo.save(numCallback)
What is going on here? function identity<T>(arg: T): T { return arg; }
Instead, we need a way of capturing the type of the argument in such a way that we can also use it to denote what is being returned. Here, we will use a type variable, a special kind of variable that works on types rather than values. function identity<T>(arg: T): T { return arg; } We've now added a type variable T to the identity function. This T allows us to capture the type the user provides (e.g. number), so that we can use that information later. Here, we use T again as the return type. On inspection, we can now see the same type is used for the argument and the return type. This allows us to traffic that type information in one side of the function and out the other. We say that this version of the identity function is generic, as it works over a range of types. Unlike using any, it's also just as precise (ie, it doesn't lose any information) as the first identity function that used numbers for the argument and return type. Once we've written the generic identity function, we can call it in one of two ways. The first way is to pass all of the arguments, including the type argument, to the function: let output = identity<string>("myString"); // type of output will be 'string' Here we explicitly set T to be string as one of the arguments to the function call, denoted using the <> around the arguments rather than ().
When should you use type assertions?
Sometimes you'll end up in a situation where you'll know more about a value than TypeScript does. Usually this will happen when you know the type of some entity could be more specific than its current type. Type assertions are a way to tell the compiler "trust me, I know what I'm doing." A type assertion is like a type cast in other languages, but performs no special checking or restructuring of data. It has no runtime impact, and is used purely by the compiler. TypeScript assumes that you, the programmer, have performed any special checks that you need. let someValue: any = "this is a string"; let strLength: number = (someValue as string).length;
Explain when to use "declare" keyword in TypeScript
The TypeScript declare keyword is used to declare variables that may not have originated from a TypeScript file. For example, lets imagine that we have a library called myLibrary that doesn't have a TypeScript declaration file and have a namespace called myLibrary in the global namespace. If you want to use that library in your TypeScript code, you can use the following code: declare var myLibrary; The type that the TypeScript runtime will give to myLibrary variable is the any type. The problem here is that you won't have Intellisense for that variable in design time but you will be able to use the library in your code. Another option to have the same behavior without using the declare keyword is just using a variable with the any type: var myLibrary: any; Both of the code examples will result in the same JavaScript output but the declare example is more readable and expresses an ambient declaration.
When should you use the never type?
The never type represents the type of values that never occur. For instance, never is the return type for a function expression or an arrow function expression that always throws an exception or one that never returns; Variables also acquire the type never when narrowed by any type guards that can never be true. The never type is a subtype of, and assignable to, every type; however, no type is a subtype of, or assignable to, never (except never itself). Even any isn't assignable to never. Some examples of functions returning never: // Function returning never must have unreachable end point function error(message: string): never { throw new Error(message); } // Inferred return type is never function fail() { return error("Something failed"); } // Function returning never must have unreachable end point function infiniteLoop(): never { while (true) { } }
What is TypeScript? Why should we use it?
TypeScript is a typed superset of JavaScript that compiles to plain JavaScript which runs on any browser or JavaScript engine. TypeScript offers support for the latest JavaScript features and also has some additional features like static typing, object oriented programming and automatic assignment of constructor.
When should you use the "any" type?
We may need to describe the type of variables that we do not know when we are writing an application. These values may come from dynamic content, e.g. from the user or a 3rd party library. In these cases, we want to opt-out of type-checking and let the values pass through compile-time checks. To do so, we label these with the any type: let notSure: any = 4; notSure = "maybe a string instead"; notSure = false; // okay, definitely a boolean The any type is a powerful way to work with existing JavaScript, allowing you to gradually opt-in and opt-out of type-checking during compilation. You might expect Object to play a similar role, as it does in other languages. But variables of type Object only allow you to assign any value to them - you can't call arbitrary methods on them, even ones that actually exist: let notSure: any = 4; notSure.ifItExists(); // okay, ifItExists might exist at runtime notSure.toFixed(); // okay, toFixed exists (but the compiler doesn't check) let prettySure: Object = 4; prettySure.toFixed(); // Error: Property 'toFixed' doesn't exist on type 'Object'. The any type is also handy if you know some part of the type, but perhaps not all of it. For example, you may have an array but the array has a mix of different types: let list: any[] = [1, true, "free"]; list[1] = 100; https://www.typescriptlang.org/docs/handbook/basic-types.html
Is this code valid? class Point { x: number; y: number; } interface Point3d extends Point { z: number; } let point3d: Point3d = {x: 1, y: 2, z: 3};
Yes, the code is valid. A class declaration creates two things: a type representing instances of the class and a constructor function. Because classes create types, you can use them in the same places you would be able to use interfaces.
What's wrong with this? declare function fn(x: any): any; declare function fn(x: HTMLElement): number; declare function fn(x: HTMLDivElement): string; var myElem: HTMLDivElement; var x = fn(myElem); // x: any, wat?
You should sort overloads by putting the more general signatures after more specific signatures: /* OK */ declare function fn(x: HTMLDivElement): string; declare function fn(x: HTMLElement): number; declare function fn(x: any): any; var myElem: HTMLDivElement; var x = fn(myElem); // x: string, :) Why: TypeScript chooses the first matching overload when resolving function calls. When an earlier overload is "more general" than a later one, the later one is effectively hidden and cannot be called.
What's wrong with this? function reverse(s: String): String;
You were using the JavaScript "non-primitive boxed object" instead of the `string` that was expected by TS. Do use the types number, string, and boolean. /* OK */ function reverse(s: string): string;
What's wrong with this? function loggingIdentity<T>(arg: T): T { console.log(arg.length); return arg; }
function loggingIdentity<T>(arg: T): T { console.log(arg.length); // Error: T doesn't have .length return arg; } When we do the above, the compiler will give us an error that we're using the .length member of arg, but nowhere have we said that arg has this member. Remember, we said earlier that these type variables stand in for any and all types, so someone using this function could have passed in a number instead, which does not have a .length member. Let's say that we've actually intended this function to work on arrays of T rather than T directly. Since we're working with arrays, the .length member should be available. We can describe this just like we would create arrays of other types: function loggingIdentity<T>(arg: T[]): T[] { console.log(arg.length); // Array has a .length, so no more error return arg; }
What is the difference between "interface vs type" statements?
interface X { a: number b: string } type X = { a: number b: string }; Answer: Unlike an interface declaration, which always introduces a named object type, a type alias declaration can introduce a name for any kind of type, including primitive, union, and intersection types. By using type instead of interface the following capabilities are lost: An interface can be named in an extends or implements clause, but a type alias for an object type literal cannot. An interface can have multiple merged declarations, but a type alias for an object type literal cannot.