Typescript: Difference between revisions
No edit summary |
|||
Line 4: | Line 4: | ||
e.g. | e.g. | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
let myString = "fred"; | let myString = "fred"; | ||
Line 16: | Line 16: | ||
Typescript supports classes and access modifiers | Typescript supports classes and access modifiers | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
class Person { | class Person { | ||
Line 86: | Line 86: | ||
== No more var == | == No more var == | ||
Don't use var but instead use | Don't use var but instead use | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts">let</syntaxhighlight> | ||
or | or | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts">const</syntaxhighlight> | ||
== Base Data Types == | == Base Data Types == | ||
Line 108: | Line 108: | ||
Union types | Union types | ||
This allows more than type e.g. | This allows more than type e.g. | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
let number_string : string | number | let number_string : string | number | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Not good if you ask me. However maybe useful for strings e.g. | Not good if you ask me. However maybe useful for strings e.g. | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
let null_string : string | null | let null_string : string | null | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Line 119: | Line 119: | ||
== Type assertions == | == Type assertions == | ||
You can assert types in one of two ways | You can assert types in one of two ways | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
let value: any = 5; | let value: any = 5; | ||
Line 126: | Line 126: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
or | or | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
let fixedString: string = (value as number).toFixed(4); | let fixedString: string = (value as number).toFixed(4); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Line 133: | Line 133: | ||
== Adding types == | == Adding types == | ||
With typescript we can specify types e.g. | With typescript we can specify types e.g. | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
function funFunc(score: number, message1: string = "default ", message2?: string): string { | function funFunc(score: number, message1: string = "default ", message2?: string): string { | ||
return message1 + message2; | return message1 + message2; | ||
Line 145: | Line 145: | ||
e.g. | e.g. | ||
For zero parameters | For zero parameters | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
let greeting = () => console.log("Hello world"); | let greeting = () => console.log("Hello world"); | ||
greeting(); // Hello world | greeting(); // Hello world | ||
Line 151: | Line 151: | ||
For 1 parameter | For 1 parameter | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
let squareit = x => x * x; | let squareit = x => x * x; | ||
let result = squareit(4); // 16 | let result = squareit(4); // 16 | ||
Line 157: | Line 157: | ||
For multiple parameters | For multiple parameters | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
let adder = (a,b) = a + b; | let adder = (a,b) = a + b; | ||
let sum = adder(2,3); // 5 | let sum = adder(2,3); // 5 | ||
Line 163: | Line 163: | ||
The example below shows a function on array (filter) which takes a function as an argument where the arguments are element, index and original array. | The example below shows a function on array (filter) which takes a function as an argument where the arguments are element, index and original array. | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
var scores = [70,125,85,110, 10000]; | var scores = [70,125,85,110, 10000]; | ||
Line 185: | Line 185: | ||
Without arrow function | Without arrow function | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
function Book() { | function Book() { | ||
let self = this; | let self = this; | ||
Line 196: | Line 196: | ||
With arrow function | With arrow function | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
function Book() { | function Book() { | ||
this.publishDate = 2016; | this.publishDate = 2016; | ||
Line 208: | Line 208: | ||
You can assign functions with the same signatures to variables with typescript. E.g. | You can assign functions with the same signatures to variables with typescript. E.g. | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
function logError(err: string) : void { | function logError(err: string) : void { | ||
Line 235: | Line 235: | ||
== Rest Parameters (params or variadic) == | == Rest Parameters (params or variadic) == | ||
Example below | Example below | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
function GetBooksReadForCustomer(name: string, ...bookIDs: number[]) { | function GetBooksReadForCustomer(name: string, ...bookIDs: number[]) { | ||
Line 246: | Line 246: | ||
You can declare several overloads for a function but implement just once. Not quite sure of the benefit but there you go. | You can declare several overloads for a function but implement just once. Not quite sure of the benefit but there you go. | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
function GetTitles(author: string) string[]; | function GetTitles(author: string) string[]; | ||
function GetTitles(author: boolean) string[]; | function GetTitles(author: boolean) string[]; | ||
Line 267: | Line 267: | ||
=== Standard === | === Standard === | ||
Basic interfaces | Basic interfaces | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
interface Employee { | interface Employee { | ||
name: string; | name: string; | ||
Line 292: | Line 292: | ||
=== Interface for Function types === | === Interface for Function types === | ||
Combining with function types | Combining with function types | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
// Simple function | // Simple function | ||
function CreateCustomerID(name: string, id: number): string { | function CreateCustomerID(name: string, id: number): string { | ||
Line 313: | Line 313: | ||
Example | Example | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
interface DamageLogger { | interface DamageLogger { | ||
(damage: string) : void; | (damage: string) : void; | ||
Line 328: | Line 328: | ||
Example below, default access is public | Example below, default access is public | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
class Developer { | class Developer { | ||
Line 370: | Line 370: | ||
====Initialise attribute without type==== | ====Initialise attribute without type==== | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
// C# ish | // C# ish | ||
class Author { | class Author { | ||
Line 389: | Line 389: | ||
====Class Expression==== | ====Class Expression==== | ||
You can create an expression of a class. E.g. implement an abstract on on the fly. | You can create an expression of a class. E.g. implement an abstract on on the fly. | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
let Newspaper = class extends ReferenceItem { | let Newspaper = class extends ReferenceItem { | ||
ImplementationOfAbstract: void { | ImplementationOfAbstract: void { | ||
Line 402: | Line 402: | ||
== Importing == | == Importing == | ||
To import typescript classes you can use the Triple-slash directive | To import typescript classes you can use the Triple-slash directive | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
/// <reference path="player.ts" /> | /// <reference path="player.ts" /> | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Line 426: | Line 426: | ||
== Interfaces and Classes == | == Interfaces and Classes == | ||
Much the same as c# as well | Much the same as c# as well | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
interface Inventory<T> { | interface Inventory<T> { | ||
getNewestItem:() => T; | getNewestItem:() => T; | ||
Line 447: | Line 447: | ||
== Constraints == | == Constraints == | ||
This is just for typescript I think | This is just for typescript I think | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
class Catalog<T extends CatalogItem> implements Inventory<T> { | class Catalog<T extends CatalogItem> implements Inventory<T> { | ||
Line 472: | Line 472: | ||
== Destructuring == | == Destructuring == | ||
Like javascript | Like javascript | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
let medals : string[] = ['gold', 'silver', 'bronze'] | let medals : string[] = ['gold', 'silver', 'bronze'] | ||
let [first, second, third] = medals; | let [first, second, third] = medals; | ||
Line 490: | Line 490: | ||
Additional to other array | Additional to other array | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
let newBookIDs = [10,20] | let newBookIDs = [10,20] | ||
let allBookIDs = [1,2,3, ...newBookIDs] // 1,2,3,10,20 | let allBookIDs = [1,2,3, ...newBookIDs] // 1,2,3,10,20 | ||
Line 497: | Line 497: | ||
== Intersection types == | == Intersection types == | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
// Previously we had union types on functions e.g. | // Previously we had union types on functions e.g. | ||
Line 523: | Line 523: | ||
== Mixins == | == Mixins == | ||
Not sure if this is worthwhile. It seems to be a form of multiple inheritance similar to C++ which most people hate. The key thing is the applyMixins which copies the functions from the base classes to the new class and is "Magic" | Not sure if this is worthwhile. It seems to be a form of multiple inheritance similar to C++ which most people hate. The key thing is the applyMixins which copies the functions from the base classes to the new class and is "Magic" | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
// Disposable Mixin | // Disposable Mixin | ||
Line 576: | Line 576: | ||
== String Literal Types and Type Aliases == | == String Literal Types and Type Aliases == | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
// Like enums | // Like enums | ||
let empCategory: 'Manager' | 'Non-Manager' | let empCategory: 'Manager' | 'Non-Manager' | ||
Line 587: | Line 587: | ||
=== Polymorphic this === | === Polymorphic this === | ||
The this is referring to the type returned. e.g. | The this is referring to the type returned. e.g. | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
class Vehicle { | class Vehicle { | ||
Line 617: | Line 617: | ||
This looks like bad news. You can merge things which you declare without saying you are doing it but by just clashing with names e.g. | This looks like bad news. You can merge things which you declare without saying you are doing it but by just clashing with names e.g. | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
interface Book { | interface Book { | ||
Line 635: | Line 635: | ||
Maybe this is a better example where an extension is built on an existing class | Maybe this is a better example where an extension is built on an existing class | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
import {UniversityLibrarian} from './classes' | import {UniversityLibrarian} from './classes' | ||
Line 653: | Line 653: | ||
==== typeof type guard ==== | ==== typeof type guard ==== | ||
Protect code for correct type | Protect code for correct type | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
if ( typeof x === 'string') | if ( typeof x === 'string') | ||
{ | { | ||
Line 670: | Line 670: | ||
==== Custom type guard ==== | ==== Custom type guard ==== | ||
You can write your own using | You can write your own using | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
function isBook(text: Book | Magazine) : text is Book { | function isBook(text: Book | Magazine) : text is Book { | ||
return (<Book>text).author !== undefined | return (<Book>text).author !== undefined | ||
Line 679: | Line 679: | ||
The data type symbol is a primitive data type. The Symbol() function returns a value of type symbol, has static properties that expose several members of built-in objects, has static methods that expose the global symbol registry, and resembles a built-in object class, but is incomplete as a constructor because it does not support the syntax "new Symbol()". | The data type symbol is a primitive data type. The Symbol() function returns a value of type symbol, has static properties that expose several members of built-in objects, has static methods that expose the global symbol registry, and resembles a built-in object class, but is incomplete as a constructor because it does not support the syntax "new Symbol()". | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
let mySymbol = Symbol('first_symbol'); | let mySymbol = Symbol('first_symbol'); | ||
Line 706: | Line 706: | ||
==== Introduction ==== | ==== Introduction ==== | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
// Class decorator | // Class decorator | ||
Line 750: | Line 750: | ||
==== Class Decorator ==== | ==== Class Decorator ==== | ||
This is the signature for a class decorator | This is the signature for a class decorator | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
// ClassDectorator Type | // ClassDectorator Type | ||
Line 760: | Line 760: | ||
Example where the constructor is not replaced | Example where the constructor is not replaced | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
export function sealed(name: string ) { | export function sealed(name: string ) { | ||
Line 777: | Line 777: | ||
Example where the constructor is replaced | Example where the constructor is replaced | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
export function logger<TFunction extends Function>(target: TFunction): TFunction { | export function logger<TFunction extends Function>(target: TFunction): TFunction { | ||
Line 798: | Line 798: | ||
==== Method Decorator ==== | ==== Method Decorator ==== | ||
Example | Example | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
export function readOnly(target : Object, | export function readOnly(target : Object, | ||
propertyKey: string, | propertyKey: string, | ||
Line 810: | Line 810: | ||
// Changing to a factory decorator | // Changing to a factory decorator | ||
// i.e. remove export and replace with return, remove function name | // i.e. remove export and replace with return, remove function name | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
export function writable(isWritable : boolean) { | export function writable(isWritable : boolean) { | ||
return function (target : Object, | return function (target : Object, | ||
Line 837: | Line 837: | ||
=== Callback functions === | === Callback functions === | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
// Create Interface (not required by nicer | // Create Interface (not required by nicer | ||
Line 888: | Line 888: | ||
* Simple API, then, catch | * Simple API, then, catch | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
function doAsyncWork(resolve, reject) { | function doAsyncWork(resolve, reject) { | ||
Line 908: | Line 908: | ||
Taking callback example | Taking callback example | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
function getBooksByCategory(cat: Category): Promise<string[]> { | function getBooksByCategory(cat: Category): Promise<string[]> { | ||
Line 949: | Line 949: | ||
===async await=== | ===async await=== | ||
Example | Example | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
async function doAsyncWork() { | async function doAsyncWork() { | ||
Line 958: | Line 958: | ||
Taking promise example | Taking promise example | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
async function logSearchResult(bookCategory: Category) { | async function logSearchResult(bookCategory: Category) { | ||
let foundBooks = await getBooksByCategory(bookCategory) | let foundBooks = await getBooksByCategory(bookCategory) | ||
Line 973: | Line 973: | ||
==Guards== | ==Guards== | ||
We can check a type by looking for a property and use the in Operator. E.g. | We can check a type by looking for a property and use the in Operator. E.g. | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
const printDocument = (doc: DelimitedDocument | PlaintextDocument) => { | const printDocument = (doc: DelimitedDocument | PlaintextDocument) => { | ||
if("seperator" in doc) { | if("seperator" in doc) { | ||
Line 984: | Line 984: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
We can provide a property (__typename) in the types to determine the type and use a predicate. E.g. | We can provide a property (__typename) in the types to determine the type and use a predicate. E.g. | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
export type = Invoice = FinalInvoice | DraftInvoice | export type = Invoice = FinalInvoice | DraftInvoice | ||
Line 995: | Line 995: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Test using asserts not assert | Test using asserts not assert | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
function assertIsNumber(val: any): asserts val is number { | function assertIsNumber(val: any): asserts val is number { | ||
if(typeof val !== "number") { | if(typeof val !== "number") { | ||
Line 1,004: | Line 1,004: | ||
==Don't Use Enums== | ==Don't Use Enums== | ||
Got this from Matt Pocock. Not a fan and preferred as const object e.g. | Got this from Matt Pocock. Not a fan and preferred as const object e.g. | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
const LOG_LEVEL = { | const LOG_LEVEL = { | ||
DEBUG: 'DEBUG', | DEBUG: 'DEBUG', | ||
Line 1,016: | Line 1,016: | ||
==Unions with Types== | ==Unions with Types== | ||
This example shows how to make a type which has known types and type which is a super set of these known types | This example shows how to make a type which has known types and type which is a super set of these known types | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
// This will hide "sm" and "xs" in the autocomplete | // This will hide "sm" and "xs" in the autocomplete | ||
type IconSize = "sm" | "xs" | string | type IconSize = "sm" | "xs" | string | ||
Line 1,025: | Line 1,025: | ||
===Introduction=== | ===Introduction=== | ||
Needed this for the omit example further down. This is where you can map a value as. For example | Needed this for the omit example further down. This is where you can map a value as. For example | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type Person = { | type Person = { | ||
name:string | name:string | ||
Line 1,042: | Line 1,042: | ||
===Using Intersection types with Mapped As=== | ===Using Intersection types with Mapped As=== | ||
Here we only want the string keys for the object because we cannot Capitialize with other types. By specifying the intersection only strings are allowed. | Here we only want the string keys for the object because we cannot Capitialize with other types. By specifying the intersection only strings are allowed. | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type Setters<Person> = { | type Setters<Person> = { | ||
[K in keyof Person & string as `set${Capitalize<K>}`]: (value: State[K]) => void | [K in keyof Person & string as `set${Capitalize<K>}`]: (value: State[K]) => void | ||
Line 1,050: | Line 1,050: | ||
==Conditional Types== | ==Conditional Types== | ||
We can breakdown complex types in to simple types. This is useful when the incoming data is more complex than we want | We can breakdown complex types in to simple types. This is useful when the incoming data is more complex than we want | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type Unarray<T> = T extends Array<infer U> ? U:T | type Unarray<T> = T extends Array<infer U> ? U:T | ||
Line 1,074: | Line 1,074: | ||
==Infer Keyword== | ==Infer Keyword== | ||
The infer keyword is used to represent the outcome on type if a generic. That is a bit of a mouthful so lets give an example. In this example R is a type to represent the return type. | The infer keyword is used to represent the outcome on type if a generic. That is a bit of a mouthful so lets give an example. In this example R is a type to represent the return type. | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any; | type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
In this example R represents the type to for the promise | In this example R represents the type to for the promise | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type PromiseReturnType<T> = T extends Promise<infer R> ? R : T | type PromiseReturnType<T> = T extends Promise<infer R> ? R : T | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Line 1,084: | Line 1,084: | ||
===Pick=== | ===Pick=== | ||
Picks keys to include in an Object | Picks keys to include in an Object | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
MyPick<T, K extends keyof T> { [k in K] : T[k] } | MyPick<T, K extends keyof T> { [k in K] : T[k] } | ||
Line 1,103: | Line 1,103: | ||
===Omit=== | ===Omit=== | ||
Omits keys from an Object | Omits keys from an Object | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type MyOmit2<T, K extends keyof T> = { [P in keyof T as P extends K ? never: P] :T[P]} | type MyOmit2<T, K extends keyof T> = { [P in keyof T as P extends K ? never: P] :T[P]} | ||
Line 1,125: | Line 1,125: | ||
==Material UI== | ==Material UI== | ||
This is new stuff probably should be with react | This is new stuff probably should be with react | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
// This lets you omit variant as a Prop | // This lets you omit variant as a Prop | ||
import React from "react" | import React from "react" | ||
Line 1,142: | Line 1,142: | ||
This is a list of challenges to make a type the supports a question. [https://github.com/type-challenges/type-challenges challenges]. This was very difficult for me to understand, maybe dyslexia, maybe just me but what they are looking for is a type which supports the line highlighted. | This is a list of challenges to make a type the supports a question. [https://github.com/type-challenges/type-challenges challenges]. This was very difficult for me to understand, maybe dyslexia, maybe just me but what they are looking for is a type which supports the line highlighted. | ||
==Example 1== | ==Example 1== | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts" highlight="7"> | ||
interface Todo { | interface Todo { | ||
title: string | title: string | ||
Line 1,158: | Line 1,158: | ||
===Example Left Side (Signature)=== | ===Example Left Side (Signature)=== | ||
So in this example we are look for a type on the left which = MyPick<Todo, 'title' | 'completed'>. Todo is a Type and 'title' and 'completed' are property keys of that type. So maybe we look at this like a function. So the function signature we need to capture the Type and the keys. | So in this example we are look for a type on the left which = MyPick<Todo, 'title' | 'completed'>. Todo is a Type and 'title' and 'completed' are property keys of that type. So maybe we look at this like a function. So the function signature we need to capture the Type and the keys. | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type MyPick<T, K extends keyof T> | type MyPick<T, K extends keyof T> | ||
</syntaxhighlight> | </syntaxhighlight> | ||
So T is the Type and K = keyof T will return a union of keys from T | So T is the Type and K = keyof T will return a union of keys from T | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type myType1 = { | type myType1 = { | ||
key1: string, | key1: string, | ||
Line 1,179: | Line 1,179: | ||
===Right Side (Implementation)=== | ===Right Side (Implementation)=== | ||
So we need to make type | So we need to make type | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
MyPick<T, K extends keyof T> | MyPick<T, K extends keyof T> | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Equal | Equal | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
const todo: TodoPreview = { | const todo: TodoPreview = { | ||
title: 'Clean room', | title: 'Clean room', | ||
Line 1,197: | Line 1,197: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
So give we have T as the type, K as the key we can do this | So give we have T as the type, K as the key we can do this | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
{ | { | ||
[k in K] : T[k] | [k in K] : T[k] | ||
Line 1,204: | Line 1,204: | ||
==Example 2 Iterating over Keys== | ==Example 2 Iterating over Keys== | ||
Ok example 2 is to make a type which makes the all properties read-only | Ok example 2 is to make a type which makes the all properties read-only | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
interface Todo { | interface Todo { | ||
title: string | title: string | ||
Line 1,216: | Line 1,216: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
So the mistake I made with this was to think they wanted what they wanted before and so this would be the answer. | So the mistake I made with this was to think they wanted what they wanted before and so this would be the answer. | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
{ | { | ||
readonly [k in K] : T[k] | readonly [k in K] : T[k] | ||
Line 1,224: | Line 1,224: | ||
===Example Left Side (Signature)=== | ===Example Left Side (Signature)=== | ||
Define a type of name MyRead=Only and one argument '''not''' two. | Define a type of name MyRead=Only and one argument '''not''' two. | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type MyReadonly<T> = | type MyReadonly<T> = | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Line 1,230: | Line 1,230: | ||
===Right Side (Implementation)=== | ===Right Side (Implementation)=== | ||
So the right-hand side is the same as the previous question except we do not have K. We don't care because we want all of the keys of type so. | So the right-hand side is the same as the previous question except we do not have K. We don't care because we want all of the keys of type so. | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
{ | { | ||
readonly [P in keyof T] : T[P] | readonly [P in keyof T] : T[P] | ||
Line 1,237: | Line 1,237: | ||
==Example 3 Iterating over Arrays transformation== | ==Example 3 Iterating over Arrays transformation== | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
const tuple = ['tesla', 'model 3', 'model X', 'model Y'] as const | const tuple = ['tesla', 'model 3', 'model X', 'model Y'] as const | ||
Line 1,246: | Line 1,246: | ||
===Example Left Side (Signature)=== | ===Example Left Side (Signature)=== | ||
So in this case the left hand side is '''almost''' provided see below | So in this case the left hand side is '''almost''' provided see below | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type TupleToObject<T extends readonly any[]> = | type TupleToObject<T extends readonly any[]> = | ||
</syntaxhighlight> | </syntaxhighlight> | ||
===Right Side (Implementation)=== | ===Right Side (Implementation)=== | ||
We need to, as ever identify the pattern which is result = array[0]: array[0], array[1]: array[1] ... array[n]: array[n]. So how do we access an array. We use an index. So | We need to, as ever identify the pattern which is result = array[0]: array[0], array[1]: array[1] ... array[n]: array[n]. So how do we access an array. We use an index. So | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
{ | { | ||
[P in T[number]] : P | [P in T[number]] : P | ||
Line 1,258: | Line 1,258: | ||
===Improving=== | ===Improving=== | ||
However in the challenge they have a test for a invalid assignment | However in the challenge they have a test for a invalid assignment | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type error = TupleToObject<[[1, 2], {}]> | type error = TupleToObject<[[1, 2], {}]> | ||
</syntaxhighlight> | </syntaxhighlight> | ||
We need to stop the any in the left hand side. So lets put on some constraints. We constrain it to be an array. | We need to stop the any in the left hand side. So lets put on some constraints. We constrain it to be an array. | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type TupleToObject<T extends readonly (string | symbol | number)[] > = { [P in T[number]] : P } | type TupleToObject<T extends readonly (string | symbol | number)[] > = { [P in T[number]] : P } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
==Example 4 First Conditional Type== | ==Example 4 First Conditional Type== | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type arr1 = ['a', 'b', 'c'] | type arr1 = ['a', 'b', 'c'] | ||
type arr2 = [3, 2, 1] | type arr2 = [3, 2, 1] | ||
Line 1,280: | Line 1,280: | ||
===Example Left Side (Signature)=== | ===Example Left Side (Signature)=== | ||
So left side was provided. In the examples we could be more specific to specify string | number for the array but we will see why not in the right hand side | So left side was provided. In the examples we could be more specific to specify string | number for the array but we will see why not in the right hand side | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type First<T extends any[]> | type First<T extends any[]> | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Line 1,289: | Line 1,289: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
This confused me at first as arr3 = [] is true and arr2 = [3, 2, 1] is false. But this is because we are actually comparing | This confused me at first as arr3 = [] is true and arr2 = [3, 2, 1] is false. But this is because we are actually comparing | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type answer1 = [] extends [] ? 'true' : 'false' // true | type answer1 = [] extends [] ? 'true' : 'false' // true | ||
type answer2 = [] extends never[] ? 'true' : 'false' // true | type answer2 = [] extends never[] ? 'true' : 'false' // true | ||
Line 1,296: | Line 1,296: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
So this is why this works. I am a better type script person for it. And the answer there is | So this is why this works. I am a better type script person for it. And the answer there is | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
T extends [] ? never : T[0] | T extends [] ? never : T[0] | ||
</syntaxhighlight> | </syntaxhighlight> | ||
==Example 5 typeof== | ==Example 5 typeof== | ||
I do not know how this one works. | I do not know how this one works. | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type tesla = ['tesla', 'model 3', 'model X', 'model Y'] | type tesla = ['tesla', 'model 3', 'model X', 'model Y'] | ||
type spaceX = ['FALCON 9', 'FALCON HEAVY', 'DRAGON', 'STARSHIP', 'HUMAN SPACEFLIGHT'] | type spaceX = ['FALCON 9', 'FALCON HEAVY', 'DRAGON', 'STARSHIP', 'HUMAN SPACEFLIGHT'] | ||
Line 1,312: | Line 1,312: | ||
===Example Left Side (Signature)=== | ===Example Left Side (Signature)=== | ||
This seemed easy and correct but it is the test cases I struggled with | This seemed easy and correct but it is the test cases I struggled with | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type Length<T extends []> | type Length<T extends []> | ||
</syntaxhighlight> | </syntaxhighlight> | ||
===Right Side (Implementation)=== | ===Right Side (Implementation)=== | ||
This is what I came up with but the subsequent test cases made it not the right answer. | This is what I came up with but the subsequent test cases made it not the right answer. | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type Length<T extends string[]> = T['length'] | type Length<T extends string[]> = T['length'] | ||
</syntaxhighlight> | </syntaxhighlight> | ||
===Answer=== | ===Answer=== | ||
So the tests cases which failed were | So the tests cases which failed were | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
Expect<Equal<Length<typeof tesla>, 4>>, | Expect<Equal<Length<typeof tesla>, 4>>, | ||
Expect<Equal<Length<typeof spaceX>, 5>>, | Expect<Equal<Length<typeof spaceX>, 5>>, | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Clearly we need to make the typeof bit work. So it appears that adding the readonly makes the type the first element in the array. | Clearly we need to make the typeof bit work. So it appears that adding the readonly makes the type the first element in the array. | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type Length<T extends readonly any[]> = T['length'] | type Length<T extends readonly any[]> = T['length'] | ||
</syntaxhighlight> | </syntaxhighlight> | ||
==Example 6 Conditional Type 2== | ==Example 6 Conditional Type 2== | ||
This one is where I hoped to answer my first one without looking but sadly not | This one is where I hoped to answer my first one without looking but sadly not | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type Result = MyExclude<'a' | 'b' | 'c', 'a'> // 'b' | 'c' | type Result = MyExclude<'a' | 'b' | 'c', 'a'> // 'b' | 'c' | ||
Line 1,338: | Line 1,338: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
But this is asking can you output types T which are not in U. We need to remember never mean omit | But this is asking can you output types T which are not in U. We need to remember never mean omit | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type MyExclude<T, U> = T extends U ? never : T | type MyExclude<T, U> = T extends U ? never : T | ||
</syntaxhighlight> | </syntaxhighlight> | ||
==Example 7 Infer== | ==Example 7 Infer== | ||
Infer allows you to name the thing you extracted and is used with conditional types. | Infer allows you to name the thing you extracted and is used with conditional types. | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type ExampleType = Promise<string> | type ExampleType = Promise<string> | ||
Line 1,367: | Line 1,367: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
This might get tricky as the answer came from [[https://www.youtube.com/watch?v=wON6MCS0NkE&list=PL_L_J_Lv0U2olJYiw2lI7SZCQrWuxmZto&index=1 youtube]]. So the first thing was to identify that from the tests they all take a generic parameter (X, Y, Z, Z1 and T). Looking at the types they are all Promises (except T) so lets make the generic and unknown promise | This might get tricky as the answer came from [[https://www.youtube.com/watch?v=wON6MCS0NkE&list=PL_L_J_Lv0U2olJYiw2lI7SZCQrWuxmZto&index=1 youtube]]. So the first thing was to identify that from the tests they all take a generic parameter (X, Y, Z, Z1 and T). Looking at the types they are all Promises (except T) so lets make the generic and unknown promise | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type MyAwaited<T extends Promise<unknown>> = any | type MyAwaited<T extends Promise<unknown>> = any | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Now we can use the infer keyword. This will solve the first two test cases X and Y. | Now we can use the infer keyword. This will solve the first two test cases X and Y. | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type MyAwaited<T extends Promise<unknown>> = T extends Promise<infer V> ? V : never | type MyAwaited<T extends Promise<unknown>> = T extends Promise<infer V> ? V : never | ||
</syntaxhighlight> | </syntaxhighlight> | ||
For the next two cases Z and Z1 this is not resolved because they unwrap promises within promises, the first one promise, the second two promises, luckily we have type to unwrap a promise so we can call this recursively. We check if the value returned from the inter is a Promise<unknown>, if it is we use our type to remove the Promise, if it isn't we return our value. | For the next two cases Z and Z1 this is not resolved because they unwrap promises within promises, the first one promise, the second two promises, luckily we have type to unwrap a promise so we can call this recursively. We check if the value returned from the inter is a Promise<unknown>, if it is we use our type to remove the Promise, if it isn't we return our value. | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type MyAwaited<T extends Promise<unknown>> = | type MyAwaited<T extends Promise<unknown>> = | ||
T extends Promise<infer V> ? | T extends Promise<infer V> ? | ||
Line 1,382: | Line 1,382: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
I could not solve the final test but in the end found an answer where they used the recursive calling of then. | I could not solve the final test but in the end found an answer where they used the recursive calling of then. | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type MyAwaited<T> = T extends { then: (onfulfilled: (arg: infer U) => unknown) => unknown; } | type MyAwaited<T> = T extends { then: (onfulfilled: (arg: infer U) => unknown) => unknown; } | ||
? MyAwaited<U> | ? MyAwaited<U> | ||
Line 1,390: | Line 1,390: | ||
==Example 8 Recursion II== | ==Example 8 Recursion II== | ||
This is apparently easy. So depressed doing these but youtube had the answer [[https://www.youtube.com/watch?v=ipwWXQQFTPU youtube]] and explained here because I need it explaining. This was the question, write an includes function. | This is apparently easy. So depressed doing these but youtube had the answer [[https://www.youtube.com/watch?v=ipwWXQQFTPU youtube]] and explained here because I need it explaining. This was the question, write an includes function. | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type isPillarMen = Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Dio'> // expected to be `false` | type isPillarMen = Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Dio'> // expected to be `false` | ||
</syntaxhighlight> | </syntaxhighlight> | ||
I did actually know the answer for the first attempt but my it was not enough. | I did actually know the answer for the first attempt but my it was not enough. | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type Includes<T extends any[], U> = U extends T[number] ? true: false | type Includes<T extends any[], U> = U extends T[number] ? true: false | ||
Line 1,415: | Line 1,415: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
The issue is with how to compare. If we look at this test case, test is equal to true and therefore fails. | The issue is with how to compare. If we look at this test case, test is equal to true and therefore fails. | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type test = Includes<[boolean, 2, 3, 5, 6, 7], false> // true | type test = Includes<[boolean, 2, 3, 5, 6, 7], false> // true | ||
</syntaxhighlight> | </syntaxhighlight> | ||
We need to use something better to compare. He googled and found this | We need to use something better to compare. He googled and found this | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type test2 = Equal<boolean, false> // false | type test2 = Equal<boolean, false> // false | ||
</syntaxhighlight> | </syntaxhighlight> | ||
So now all we need to do is to iterate using our new approach to compare. But first a reminder. With type script we can obtain the first or second element and the rest using the spread operator. This is the same for types | So now all we need to do is to iterate using our new approach to compare. But first a reminder. With type script we can obtain the first or second element and the rest using the spread operator. This is the same for types | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
let [first, ...rest] = [1, 2, 3, 4]; | let [first, ...rest] = [1, 2, 3, 4]; | ||
let [first,second ...rest] = [1, 2, 3, 4]; | let [first,second ...rest] = [1, 2, 3, 4]; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
So what we do below is we separate the first element in First off from the Rest of the elements. If equal all good, if not call recursively with the rest until there are no more elements. One last thing, the youtuber also thought this was not a simple challenge | So what we do below is we separate the first element in First off from the Rest of the elements. If equal all good, if not call recursively with the rest until there are no more elements. One last thing, the youtuber also thought this was not a simple challenge | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type Includes<T extends any[], U> = T extends [infer First, ...infer Rest] ? | type Includes<T extends any[], U> = T extends [infer First, ...infer Rest] ? | ||
(Equal<U, First> extends true ? true : Includes<Rest, U>) : false | (Equal<U, First> extends true ? true : Includes<Rest, U>) : false | ||
Line 1,434: | Line 1,434: | ||
==Example 9 Parameters== | ==Example 9 Parameters== | ||
So Given | So Given | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
const foo = (arg1: string, arg2: number): void => {} | const foo = (arg1: string, arg2: number): void => {} | ||
const bar = (arg1: boolean, arg2: { a: 'A' }): void => {} | const bar = (arg1: boolean, arg2: { a: 'A' }): void => {} | ||
Line 1,451: | Line 1,451: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
So we are passing (): void => {} so we should be able to capture the input as '''(...args: T) => any''' | So we are passing (): void => {} so we should be able to capture the input as '''(...args: T) => any''' | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type MyParameters<T> = T extends ( (...args: infer U) => any ) ? U : never | type MyParameters<T> = T extends ( (...args: infer U) => any ) ? U : never | ||
</syntaxhighlight> | </syntaxhighlight> | ||
==Example 9 Parameters== | ==Example 9 Parameters== | ||
So Given | So Given | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
const foo = (arg1: string, arg2: number): void => {} | const foo = (arg1: string, arg2: number): void => {} | ||
const bar = (arg1: boolean, arg2: { a: 'A' }): void => {} | const bar = (arg1: boolean, arg2: { a: 'A' }): void => {} | ||
Line 1,473: | Line 1,473: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
So we are passing (): void => {} so we should be able to capture the input as '''(...args: T) => any''' | So we are passing (): void => {} so we should be able to capture the input as '''(...args: T) => any''' | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type MyParameters<T> = T extends ( (...args: infer U) => any ) ? U : never | type MyParameters<T> = T extends ( (...args: infer U) => any ) ? U : never | ||
</syntaxhighlight> | </syntaxhighlight> | ||
==Example 10 Return Value== | ==Example 10 Return Value== | ||
Possibly getting easier. Copying is getting quicker and now I used my learning today so not a waste. | Possibly getting easier. Copying is getting quicker and now I used my learning today so not a waste. | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
const fn = (v: boolean) => { | const fn = (v: boolean) => { | ||
if (v) | if (v) | ||
Line 1,500: | Line 1,500: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
If we look at the examples for the test cases and switching them around we see a pattern | If we look at the examples for the test cases and switching them around we see a pattern | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
MyReturnType<() => string | MyReturnType<() => string | ||
MyReturnType<() => 123 | MyReturnType<() => 123 | ||
Line 1,510: | Line 1,510: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
The last two look slightly different but they are not. typeof fn just means | The last two look slightly different but they are not. typeof fn just means | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
fn() => number | fn() => number | ||
fn1() => number | fn1() => number | ||
</syntaxhighlight> | </syntaxhighlight> | ||
So the answer is | So the answer is | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type MyReturnType<T extends Function> = | type MyReturnType<T extends Function> = | ||
T extends (...args: any) => infer R | T extends (...args: any) => infer R | ||
Line 1,522: | Line 1,522: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
==Example 11 MyReadonly2== | ==Example 11 MyReadonly2== | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
interface Todo1 { | interface Todo1 { | ||
title: string | title: string | ||
Line 1,550: | Line 1,550: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
This was though. The first thing to notice is the first test case. For this we need to remember we can have default arguments in typescript | This was though. The first thing to notice is the first test case. For this we need to remember we can have default arguments in typescript | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type MyReadonly2<T, K extends keyof T = keyof T> = any | type MyReadonly2<T, K extends keyof T = keyof T> = any | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Next we need to understand intersection which is the & sign. This will combine the sets in the braces. | Next we need to understand intersection which is the & sign. This will combine the sets in the braces. | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type MyReadonly2<T, K extends keyof T = keyof T> = | type MyReadonly2<T, K extends keyof T = keyof T> = | ||
{ SET A } & | { SET A } & | ||
Line 1,560: | Line 1,560: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Set A is all of the keys specified. | Set A is all of the keys specified. | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type MyReadonly2<T, K extends keyof T = keyof T> = | type MyReadonly2<T, K extends keyof T = keyof T> = | ||
{ readonly [P in K] : T[P] } & | { readonly [P in K] : T[P] } & | ||
Line 1,566: | Line 1,566: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Set B is just excluding the value which are not in K. This is the same as the Omit. Understand the Mapped As and this is easy | Set B is just excluding the value which are not in K. This is the same as the Omit. Understand the Mapped As and this is easy | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type MyReadonly2<T, K extends keyof T = keyof T> = | type MyReadonly2<T, K extends keyof T = keyof T> = | ||
{ readonly [P in K] : T[P] } & | { readonly [P in K] : T[P] } & | ||
Line 1,573: | Line 1,573: | ||
==Example 12 Deep Readonly and Recursion== | ==Example 12 Deep Readonly and Recursion== | ||
This was going so well as I understood recursion. Simply add read only to the object | This was going so well as I understood recursion. Simply add read only to the object | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type X1 = { | type X1 = { | ||
a: () => 22 | a: () => 22 | ||
Line 1,621: | Line 1,621: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
So all we have to do it tell if it is an object or keyof | So all we have to do it tell if it is an object or keyof | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="ts"> | ||
type DeepReadonly<T> = keyof T extends never | type DeepReadonly<T> = keyof T extends never |
Revision as of 00:23, 23 July 2023
Introduction
TypeScript is a typed language which produces javascript.
e.g.
let myString = "fred";
let myBoolean = true;
function createMessage(name:string) {
}
Typescript supports classes and access modifiers
class Person {
name: string
lastName: string
public Person(name:string) {
this.name = name;
}
public void setLastName(lastName: string) {
this.lastName = lastName;
}
}
Configuration
tsconfig
You can set the options for the compiler you can specify a tsconfig.json file. By using
tsc --init
you get a default file.
You can inherit tsconfigs from parent directories. This compiles all *.ts files in this directory and child directories.
{
"extends": "../tsconfig.base",
"compilerOptions": {
"removeComments": true
},
"include": [
"./**/*.ts"
]
}
Webpack Configuration
The ts-loader module allows recompiling of the type script and you need to install it if using.
module.exports = {
entry: './app/app.ts',
devtool: 'inline-source-map'
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/
}
]
},
resolve: {
extensions: ['.tsx', '.ts', 'js']
},
output: {
filename: 'bundle.js'
},
devServer: {
inline: false
}
};
Data Types
No more var
Don't use var but instead use
let
or
const
Base Data Types
The following types are available
- Boolean
- Number
- String
- Array
- Enum (not in javascript e.g. enum Category {biology, Poetry, Fiction})
- Tuple (e.g. let myTuple: [number, string] = [25,'truck'] not other elements can have only number of string e.g. myTuple[2] = 'fred')
Other types
- void
- null
- undefined
- Never (e.g. for infinite loop return types)
- Any (e.g. for when using types not guaranteed from other libraries)
Union types This allows more than type e.g.
let number_string : string | number
Not good if you ask me. However maybe useful for strings e.g.
let null_string : string | null
By default the null is not allowed to be assigned without a union declaration.
Type assertions
You can assert types in one of two ways
let value: any = 5;
let fixedString: string = (<number>value).toFixed(4);
console.log(fixedString); // 5.0000
or
let fixedString: string = (value as number).toFixed(4);
Functions
Adding types
With typescript we can specify types e.g.
function funFunc(score: number, message1: string = "default ", message2?: string): string {
return message1 + message2;
}
The ? means the parameters is option and the final colon shows the return value of the function
Arrow Functions or lamdas
These take the format of
parameters => function body
e.g. For zero parameters
let greeting = () => console.log("Hello world");
greeting(); // Hello world
For 1 parameter
let squareit = x => x * x;
let result = squareit(4); // 16
For multiple parameters
let adder = (a,b) = a + b;
let sum = adder(2,3); // 5
The example below shows a function on array (filter) which takes a function as an argument where the arguments are element, index and original array.
var scores = [70,125,85,110, 10000];
var highscores = scores.filter((element, index, array) => {
var result = false
if (index === 0) {
console.log("arrrrayyy", array)
result = true;
}
if(element > 100) {
result = true;
}
return result;
});
console.log("test");
console.log("iain", highscores);
Another example,
Without arrow function
function Book() {
let self = this;
self.publishDate = 2016;
setInterval(function() {
console.log(self.publishDate);
}, 1000)
}
With arrow function
function Book() {
this.publishDate = 2016;
setInterval(() = > {
console.log(this.publishDate);
}, 1000)
}
function types (delegates)
You can assign functions with the same signatures to variables with typescript. E.g.
function logError(err: string) : void {
console.error(err);
}
function logLog(err: string) : void {
console.log(err);
}
let logger : (value: string) => void;
if(x === 1)
{
logger = logError;
}
else
{
logger = logLog;
}
logger('Score: ${x}');
Rest Parameters (params or variadic)
Example below
function GetBooksReadForCustomer(name: string, ...bookIDs: number[]) {
}
let books = GetBooksReadForCustomer('Bob', 1,2,3);
Function Overloads
You can declare several overloads for a function but implement just once. Not quite sure of the benefit but there you go.
function GetTitles(author: string) string[];
function GetTitles(author: boolean) string[];
function GetTitles(author: any) string[] {
if(typeof bookProperty == 'string') {
// do stuff
}
else if(typeof bookProperty == 'boolean') {
// do stuff
}
return 'stuff';
}
Custom types
Typescript supports classes and interfaces
Interfaces
Standard
Basic interfaces
interface Employee {
name: string;
title: string;
}
interface Manager extends Employee {
department : string;
numberOfEmployees: number;
scheduleMeeting: (topic: string) => void;
}
let developer = {
name: 'iain',
title: 'GDB',
editor: 'Visual Studio Code'
}
let newEmployee: Employee = developer;
Interface for Function types
Combining with function types
// Simple function
function CreateCustomerID(name: string, id: number): string {
return name + id;
}
// Define an interface
interface StringGenerator {
(chars: string, nums: number): string;
}
// Old way
let IdGenerator: (chars: string, nums: number) => string;
IdGenerator = CreateCustomerID;
// Improved way
let IdGenerator = StringGenerator;
Example
interface DamageLogger {
(damage: string) : void;
}
let logDamage: DamageLogger;
logDamage = (damage: string) => console.log('Damage reported: ' + damage);
logDamage('coffee stains'); // Damage reported: coffee stains
Classes
Basic Stuff
Example below, default access is public
class Developer {
department: string;
private _title: string;
get title(): string {
return this._title;
}
set title(newTitle: string) {
this._title = newTitle.toUppperCase();
}
// Static members and attributes exist
static could_be_a_const: string = 'Hello me';
static logMe() {
console.log('Hello');
}
}
// Extending
class WebDeveloper extends Developer {
readonly favoriteEditor: string
constructor(editor: string) {
super();
this.favoriteEditor = editor;
}
}
// Abstract
class MyClass {
abstract printStuff(): void;
}
Non C# Stuff
Initialise attribute without type
// C# ish
class Author {
name: string;
constructor(inName: string) {
name = inName;
}
}
// Typescript
class Author {
constructor(public name: string) {
}
}
Class Expression
You can create an expression of a class. E.g. implement an abstract on on the fly.
let Newspaper = class extends ReferenceItem {
ImplementationOfAbstract: void {
console.log('I am implemented now');
}
}
let myPaper = new Newspaper('The Gazette', 2016);
myPaper.ImplementationOfAbstract();
Importing
To import typescript classes you can use the Triple-slash directive
/// <reference path="player.ts" />
Generics
Array
Array is a built in Generic e.g.
let Books : Book[]
// With Generic Array
let Books : Array<Book>
Functions
Much the same as c#
function LogAndReturn<T>)thing : T) : T {
console.log(thing);
return thing;
}
let someString : string = LogAndReturn<string>('log this');
Interfaces and Classes
Much the same as c# as well
interface Inventory<T> {
getNewestItem:() => T;
addItem: (newItem: T) => void;
getAllItems: () => Array<T>;
}
class Catalog<T> implements Inventory<T> {
private catalogItems = new Array<T>();
addItem(newItem: T)_ {
this.catalogItems.push(newItem);
}
...
}
let bookCatalog = new Catalog<Book>();
Constraints
This is just for typescript I think
class Catalog<T extends CatalogItem> implements Inventory<T> {
// Only types satisfying the extends constraint CatalogItem
// are allowed at compile time. Seems a bit constraining to me.
}
TypeScript Declaration Files
These are typescript wrappers for JavaScript libraries. This allows the typescript compiler to validate your usage.
These will have the extension .d.ts and you can find these on GitHub at definitely typed. Note these may sometimes be out of date.
Search here
npm allows you to install these using
npm install --save @types/lodash
Advanced
Destructuring
Like javascript
let medals : string[] = ['gold', 'silver', 'bronze']
let [first, second, third] = medals;
let person = {
name: 'Audrey',
address: '123 Main St',
phone: '555:1212'
}
let {name, address, phone} = person
Spread Operator
Like javascript
Additional to other array
let newBookIDs = [10,20]
let allBookIDs = [1,2,3, ...newBookIDs] // 1,2,3,10,20
Intersection types
// Previously we had union types on functions e.g.
function test(inArg : number | string) : void {
}
// Now we have Intersection types where all the members
// of the types are combined
function test() : Book & Magazine {
}
// So without publish which is a member of magazine it
// will not compile as it checks all members exist
let serialNovel: Book & Magazine = {
id; 100,
title: 'The Gradual Tale',
author: 'Occasional Pen'
// publisher: 'Serial Press'
}
Mixins
Not sure if this is worthwhile. It seems to be a form of multiple inheritance similar to C++ which most people hate. The key thing is the applyMixins which copies the functions from the base classes to the new class and is "Magic"
// Disposable Mixin
class Disposable {
isDisposed: boolean;
dispose() {
this.isDisposed = true;
}
}
// Activatable Mixin
class Activatable {
isActive: boolean;
activate() {
this.isActive = true;
}
deactivate() {
this.isActive = false;
}
}
class SmartObject {
constructor() {
setInterval(() => console.log(this.isActive + " : " + this.isDisposed), 500);
}
interact() {
this.activate();
}
}
interface SmartObject extends Disposable, Activatable {}
applyMixins(SmartObject, [Disposable, Activatable]);
let smartObj = new SmartObject();
setTimeout(() => smartObj.interact(), 1000);
////////////////////////////////////////
// In your runtime library somewhere
////////////////////////////////////////
function applyMixins(derivedCtor: any, baseCtors: any[]) {
baseCtors.forEach(baseCtor => {
Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
Object.defineProperty(derivedCtor.prototype, name, Object.getOwnPropertyDescriptor(baseCtor.prototype, name));
});
});
}
String Literal Types and Type Aliases
// Like enums
let empCategory: 'Manager' | 'Non-Manager'
// Type aliases
type EmployeeCategory = 'Manager' | 'Non-Manager'
Advanced Type Features
Polymorphic this
The this is referring to the type returned. e.g.
class Vehicle {
Drive() {
return this
}
}
class Car extends Vehicle {
CarryPeople() {
return this
}
}
class Truck extends Vehicle {
CarryCargo() {
return this
}
}
let t = new Truck();
t.Drive() // returns a Truck object
Basically we are to understand that the this is the this of the type originally declared not the this of the function in this case Vehicle.
Declaration Merging
This looks like bad news. You can merge things which you declare without saying you are doing it but by just clashing with names e.g.
interface Book {
id: number,
author: string,
category: Category
}
// By typing another they are merged by default
interface Book {
identifier: number,
writer: string
}
Maybe this is a better example where an extension is built on an existing class
import {UniversityLibrarian} from './classes'
declare module './classes' {
interface UniversityLibrarian {
phone: string;
hostSeminar(topic: string): void;
}
}
UniversityLibrarian.prototype.hostSeminar = function(topic) {
console.log('Hosting a seminar on ' + topic)
}
Type Guards
typeof type guard
Protect code for correct type
if ( typeof x === 'string')
{
}
else if ( typeof x === 'number')
{
}
Allows types are
- string
- number
- boolean
- symbol
Custom type guard
You can write your own using
function isBook(text: Book | Magazine) : text is Book {
return (<Book>text).author !== undefined
}
Symbols
The data type symbol is a primitive data type. The Symbol() function returns a value of type symbol, has static properties that expose several members of built-in objects, has static methods that expose the global symbol registry, and resembles a built-in object class, but is incomplete as a constructor because it does not support the syntax "new Symbol()".
let mySymbol = Symbol('first_symbol');
const CLASS_INFO = Symbol();
class myClass
{
[CLASS_INFO](): void {
console.log('This is my class');
}
static [Symbol.hasInstance](obj: Object) : boolean {
...
}
}
let aClass = new myClass(0;
aClass[CLASS_INFO]() // This is my class
May need to do some reading on this
Decorators
Introduction
// Class decorator
// target = Constructor function for the class
function ui_element(target: Function) { // do ui stuff}
// Method decorator
// Parameters are
// t = constructor function for a static method
or prototype for the class if it is an instance member
// p = name of the decorated member
// d = Property descriptor for the member
function my_deprecated(t: any, p: string, d: PropertyDescriptor)
{
console.log('This method will go away soon...');
}
@ui_element
class {
@my_deprecated
someOldMethod() { }
}
// Decorator Factories
function ui_element(element: string) {
return function(target: Function) {
console.log('Create new element : ${element}');
}
}
// Usage
@ui_element('Simple Form')
class ContactForm {
// contact properties
}
Class Decorator
This is the signature for a class decorator
// ClassDectorator Type
<TFunction extends Function>(target: TFunction) => TFunction | void
Example where the constructor is not replaced
export function sealed(name: string ) {
return function(target: Function): void {
console.log('Sealing the constructor: ${name}')
Object.seal(target);
Object.seal(target.prototype)
}
}
@sealed('Class Library')
class Boris {
}
Example where the constructor is replaced
export function logger<TFunction extends Function>(target: TFunction): TFunction {
// Create a Function type
let newConstructor: Function = function() {
console.log('Creating new instance')
console.log(target);
}
// Assign protype and constructor from original
newConstructor.prototype = Object.create(target.prototype);
newConstructor.prototype.constructor = target;
// Return the new function
return <TFunction>newConstructor;
}
Method Decorator
Example
export function readOnly(target : Object,
propertyKey: string,
descriptor: PropertyDescriptor) {
console.log('Setting ${propertyKey}.');
descriptor.writable = false;
}
// Changing to a factory decorator // i.e. remove export and replace with return, remove function name
export function writable(isWritable : boolean) {
return function (target : Object,
propertyKey: string,
descriptor: PropertyDescriptor) {
console.log('Setting ${propertyKey}.');
descriptor.writable = isWritable;
}
}
..
class aClass
{
@writable(false);
testMethod() :void {
console.log("I am a test method")
}
}
..
Asyncronous Calls
Callback functions
// Create Interface (not required by nicer
interface LibMgrCallBack {
// pararameters : return args
(err: Error, titles: string[]) : void
}
function getBooksByCategory(cat: Category, inCallBack: LibMgrCallBack) : void {
// Fake function
setTimeout( () => {
try {
let foundBooks: string[] = Util.GetBooks(cat);
if(foundBooks.length > 0) {
callback(nuT^ll, foundBooks);
}
else {
throw new Error('No Books Found');
}
}
catch(error) {
}
}, 2000);
}
function logCategorySearch(err: Error, titles: string[]) : void {
if(err) {
console.log('Error Message: ${err.message}');
}
else {
console.log('Found following titles');
console.log(titles);
}
}
console.log('Begin')'Found titles: ${titles}'))
getBooksByCategory(Category.Fiction, logCategorySearch);
console.log('Submmitted')
Promises
So,
- Requires 2015
- Similar to Tasks in c#.
- You can chain promises togethe'Found titles: ${titles}'))r
- Simple API, then, catch
function doAsyncWork(resolve, reject) {
// Perform Async work
if(success) resolve(data)'Found titles: ${titles}'))
else reject(reason)
}
let p: Promise<string> = new Promise(doAsyncWork);
// Alternate and more realistic
let p: Promise<string> = new Promise( (resolve, reject) => {
// Perform Async work
if(success) resolve(data)
else reject(reason)
})
Taking callback example
function getBooksByCategory(cat: Category): Promise<string[]> {
let p: Promise<string[]> = new Promise((resolve, reject) => {
setTimeout( () => {
let foundBooks: string[] = Util.GetBooks(cat);
if(foundBooks.length > 0) {
resolve(foundBooks);
}
else {
reject('No Books Found');
}
}, 2000);
});
}
console.log('Begin')
getBooksByCategory(Category.Fiction)
.then(
titles => {
console.log('Found titles: ${titles}');
throw 'something bad happened'; // Force exception
return titles.length;
}, reason = { return 0;})
.then(numOfBooks => console'Found titles: ${titles}')).log('Number Of Books found: ${numOfBooks}')) // Chained
.catch(reason => console.log('Found titles: ${reason}'))
console.log('Beginning')
logSearchResults(Category.Fiction);
console.log('Submitted')
async await
Example
async function doAsyncWork() {
let results = await GetLongTask();
console.log(results)
}
Taking promise example
async function logSearchResult(bookCategory: Category) {
let foundBooks = await getBooksByCategory(bookCategory)
console.log(foundBooks)
}
console.log('Beginning')
logSearchResults(Category.Fiction);
console.log('Submitted')
Tips For TypeScript
Guards
We can check a type by looking for a property and use the in Operator. E.g.
const printDocument = (doc: DelimitedDocument | PlaintextDocument) => {
if("seperator" in doc) {
printDeliminated(doc)
}
else {
printPlaintext(doc)
}
};
We can provide a property (__typename) in the types to determine the type and use a predicate. E.g.
export type = Invoice = FinalInvoice | DraftInvoice
export const isTypeFinalInvoice = (invoice: Invoice): invoice is FinalInvoice => {
return invoice.__typename === "FinalInvoice";
}
export const isTypeDaftInvoice = (invoice: Invoice): invoice is DaftInvoice => {
return invoice.__typename === "DaftInvoice";
}
Test using asserts not assert
function assertIsNumber(val: any): asserts val is number {
if(typeof val !== "number") {
throw new AssertionError("Not a number"
}
)
Don't Use Enums
Got this from Matt Pocock. Not a fan and preferred as const object e.g.
const LOG_LEVEL = {
DEBUG: 'DEBUG',
WARNING: 'Warning',
ERROR: 'Error'
} as const
type LogLevel = ObjectValues<typeof LOG_LEVEL>
Unions with Types
This example shows how to make a type which has known types and type which is a super set of these known types
// This will hide "sm" and "xs" in the autocomplete
type IconSize = "sm" | "xs" | string
// Better to do
type IconSize = "sm" | "xs" | Omit<string, "xs" | "sm">
Mapped As
Introduction
Needed this for the omit example further down. This is where you can map a value as. For example
type Person = {
name:string
age: number
}
type Setters = {
[K in keyof Person as `set${Capitalize<K>}`]: (value: State[K]) => void
}
// type Setters = {
// setName: (value: string) => void
// setAge: (value: number) => void
// }
Using Intersection types with Mapped As
Here we only want the string keys for the object because we cannot Capitialize with other types. By specifying the intersection only strings are allowed.
type Setters<Person> = {
[K in keyof Person & string as `set${Capitalize<K>}`]: (value: State[K]) => void
}
Conditional Types
We can breakdown complex types in to simple types. This is useful when the incoming data is more complex than we want
type Unarray<T> = T extends Array<infer U> ? U:T
type Release = Unarry<typeof backlog["releases"]>
type Epic = Unarray<Release["epics"]>
const backlog = {
releases: [
name: "Sprint 1",
epic: [
{
name: "Account Management",
tasks: [
{ "name": "Single Sign on", Priority.mustHave },
{ "name": "Email Notification", Priority.mustHave },
]
}
]
]
}
Infer Keyword
The infer keyword is used to represent the outcome on type if a generic. That is a bit of a mouthful so lets give an example. In this example R is a type to represent the return type.
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
In this example R represents the type to for the promise
type PromiseReturnType<T> = T extends Promise<infer R> ? R : T
Some Key examples
Pick
Picks keys to include in an Object
MyPick<T, K extends keyof T> { [k in K] : T[k] }
interface Todo {
title: string
description: string
completed: boolean
}
type TodoPreview = MyPick<Todo, 'title' | 'completed'>
const todo: TodoPreview = {
title: 'Clean room',
completed: false,
}
Omit
Omits keys from an Object
type MyOmit2<T, K extends keyof T> = { [P in keyof T as P extends K ? never: P] :T[P]}
interface Todo {
title: string
description: string
completed: boolean
}
type TodoPreview = MyOmit<Todo, 'description' | 'title'>
const todo: TodoPreview = {
completed: false,
}
Functional Overloads
Did not know this was possible but it is
Material UI
This is new stuff probably should be with react
// This lets you omit variant as a Prop
import React from "react"
import {Button, ButtonProps } from "matierial-ui/core"
type Props = Omit<ButtonProps, "variant">
const BrandButton: React.FC<Props> = ({children, ...rest }) => {
return <Button {...rest, children} />
};
export default BrandButton;
Typescript Challenges
Introduction
This is a list of challenges to make a type the supports a question. challenges. This was very difficult for me to understand, maybe dyslexia, maybe just me but what they are looking for is a type which supports the line highlighted.
Example 1
interface Todo {
title: string
description: string
completed: boolean
}
type TodoPreview = MyPick<Todo, 'title' | 'completed'>
const todo: TodoPreview = {
title: 'Clean room',
completed: false,
}
Example Left Side (Signature)
So in this example we are look for a type on the left which = MyPick<Todo, 'title' | 'completed'>. Todo is a Type and 'title' and 'completed' are property keys of that type. So maybe we look at this like a function. So the function signature we need to capture the Type and the keys.
type MyPick<T, K extends keyof T>
So T is the Type and K = keyof T will return a union of keys from T
type myType1 = {
key1: string,
key2: string,
key3: string
}
type K = keyof myType1 // Returns type with values 'key1', 'key2' nad 'key3'
// So we can we can assigned these values
const myVar1: K = 'key1'
const myVar2: K = 'key2'
const myVar3: K = 'rubbish' // Error
Right Side (Implementation)
So we need to make type
MyPick<T, K extends keyof T>
Equal
const todo: TodoPreview = {
title: 'Clean room',
completed: false,
}
So we need to make an object that is a list (array) of keys title,completed and the a list (array) of values associated with that key.
{
array of keys : value for key for this type
keys[]: values[]
}
So give we have T as the type, K as the key we can do this
{
[k in K] : T[k]
}
Example 2 Iterating over Keys
Ok example 2 is to make a type which makes the all properties read-only
interface Todo {
title: string
description: string
}
const todo: MyReadonly<Todo> = {
title: "Hey",
description: "foobar"
}
So the mistake I made with this was to think they wanted what they wanted before and so this would be the answer.
{
readonly [k in K] : T[k]
}
But this is wrong because the challenges tell you the arguments and in this challenge there is only one argument - the type, no list of keys. Read the question. Going with the left and the right
Example Left Side (Signature)
Define a type of name MyRead=Only and one argument not two.
type MyReadonly<T> =
Right Side (Implementation)
So the right-hand side is the same as the previous question except we do not have K. We don't care because we want all of the keys of type so.
{
readonly [P in keyof T] : T[P]
}
Example 3 Iterating over Arrays transformation
const tuple = ['tesla', 'model 3', 'model X', 'model Y'] as const
type result = TupleToObject<typeof tuple> // expected { 'tesla': 'tesla', 'model 3': 'model 3', 'model X': 'model X', 'model Y': 'model Y'}
type TupleToObject<T extends readonly any[]> = any
Example Left Side (Signature)
So in this case the left hand side is almost provided see below
type TupleToObject<T extends readonly any[]> =
Right Side (Implementation)
We need to, as ever identify the pattern which is result = array[0]: array[0], array[1]: array[1] ... array[n]: array[n]. So how do we access an array. We use an index. So
{
[P in T[number]] : P
}
Improving
However in the challenge they have a test for a invalid assignment
type error = TupleToObject<[[1, 2], {}]>
We need to stop the any in the left hand side. So lets put on some constraints. We constrain it to be an array.
type TupleToObject<T extends readonly (string | symbol | number)[] > = { [P in T[number]] : P }
Example 4 First Conditional Type
type arr1 = ['a', 'b', 'c']
type arr2 = [3, 2, 1]
type arr3 = []
type head1 = First<arr1> // expected to be 'a'
type head2 = First<arr2> // expected to be 3
type head3 = First<arr3> // expected to be never
type First<T extends any[]> = any
Example Left Side (Signature)
So left side was provided. In the examples we could be more specific to specify string | number for the array but we will see why not in the right hand side
type First<T extends any[]>
Right Side (Implementation)
This is the first conditional type. For this we use the extends keyword which is in the form of SomeType extends OtherType ? x : y and defined by
If SomeType extends another given type OtherType, then ConditionalType is TrueType, otherwise it is FalseType
This confused me at first as arr3 = [] is true and arr2 = [3, 2, 1] is false. But this is because we are actually comparing
type answer1 = [] extends [] ? 'true' : 'false' // true
type answer2 = [] extends never[] ? 'true' : 'false' // true
type answer3 = [1,2,3] extends number[] ? 'true' : 'false' // true
type answer4 = [1,2,3] extends [] ? 'true' : 'false' // false
So this is why this works. I am a better type script person for it. And the answer there is
T extends [] ? never : T[0]
Example 5 typeof
I do not know how this one works.
type tesla = ['tesla', 'model 3', 'model X', 'model Y']
type spaceX = ['FALCON 9', 'FALCON HEAVY', 'DRAGON', 'STARSHIP', 'HUMAN SPACEFLIGHT']
type teslaLength = Length<tesla> // expected 4
type spaceXLength = Length<spaceX> // expected 5
type Length<T> = any
Example Left Side (Signature)
This seemed easy and correct but it is the test cases I struggled with
type Length<T extends []>
Right Side (Implementation)
This is what I came up with but the subsequent test cases made it not the right answer.
type Length<T extends string[]> = T['length']
Answer
So the tests cases which failed were
Expect<Equal<Length<typeof tesla>, 4>>,
Expect<Equal<Length<typeof spaceX>, 5>>,
Clearly we need to make the typeof bit work. So it appears that adding the readonly makes the type the first element in the array.
type Length<T extends readonly any[]> = T['length']
Example 6 Conditional Type 2
This one is where I hoped to answer my first one without looking but sadly not
type Result = MyExclude<'a' | 'b' | 'c', 'a'> // 'b' | 'c'
type MyExclude<T, U> = any
But this is asking can you output types T which are not in U. We need to remember never mean omit
type MyExclude<T, U> = T extends U ? never : T
Example 7 Infer
Infer allows you to name the thing you extracted and is used with conditional types.
type ExampleType = Promise<string>
type Result = MyAwaited<ExampleType> // string
// Make a type for this
type MyAwaited<T> = any
// Which solves the following
type X = Promise<string>
type Y = Promise<{ field: number }>
type Z = Promise<Promise<string | number>>
type Z1 = Promise<Promise<Promise<string | boolean>>>
type T = { then: (onfulfilled: (arg: number) => any) => any }
type cases = [
Expect<Equal<MyAwaited<X>, string>>,
Expect<Equal<MyAwaited<Y>, { field: number }>>,
Expect<Equal<MyAwaited<Z>, string | number>>,
Expect<Equal<MyAwaited<Z1>, string | boolean>>,
Expect<Equal<MyAwaited<T>, number>>,
]
This might get tricky as the answer came from [youtube]. So the first thing was to identify that from the tests they all take a generic parameter (X, Y, Z, Z1 and T). Looking at the types they are all Promises (except T) so lets make the generic and unknown promise
type MyAwaited<T extends Promise<unknown>> = any
Now we can use the infer keyword. This will solve the first two test cases X and Y.
type MyAwaited<T extends Promise<unknown>> = T extends Promise<infer V> ? V : never
For the next two cases Z and Z1 this is not resolved because they unwrap promises within promises, the first one promise, the second two promises, luckily we have type to unwrap a promise so we can call this recursively. We check if the value returned from the inter is a Promise<unknown>, if it is we use our type to remove the Promise, if it isn't we return our value.
type MyAwaited<T extends Promise<unknown>> =
T extends Promise<infer V> ?
(V extends Promise<unknown> ? MyAwaited<V>: V) :
never
I could not solve the final test but in the end found an answer where they used the recursive calling of then.
type MyAwaited<T> = T extends { then: (onfulfilled: (arg: infer U) => unknown) => unknown; }
? MyAwaited<U>
: T;
Would have like to have seen the equivalent solution for this.
Example 8 Recursion II
This is apparently easy. So depressed doing these but youtube had the answer [youtube] and explained here because I need it explaining. This was the question, write an includes function.
type isPillarMen = Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Dio'> // expected to be `false`
I did actually know the answer for the first attempt but my it was not enough.
type Includes<T extends any[], U> = U extends T[number] ? true: false
// Many of the tests passed
Expect<Equal<Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Kars'>, true>>,
Expect<Equal<Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Dio'>, false>>,
Expect<Equal<Includes<[1, 2, 3, 5, 6, 7], 7>, true>>,
Expect<Equal<Includes<[1, 2, 3, 5, 6, 7], 4>, false>>,
Expect<Equal<Includes<[1, 2, 3], 2>, true>>,
Expect<Equal<Includes<[1, 2, 3], 1>, true>>,
// But many did not
Expect<Equal<Includes<[{}], { a: 'A' }>, false>>,
Expect<Equal<Includes<[boolean, 2, 3, 5, 6, 7], false>, false>>,
Expect<Equal<Includes<[true, 2, 3, 5, 6, 7], boolean>, false>>,
Expect<Equal<Includes<[{ a: 'A' }], { readonly a: 'A' }>, false>>,
Expect<Equal<Includes<[{ readonly a: 'A' }], { a: 'A' }>, false>>,
Expect<Equal<Includes<[1], 1 | 2>, false>>,
Expect<Equal<Includes<[1 | 2], 1>, false>>,
The issue is with how to compare. If we look at this test case, test is equal to true and therefore fails.
type test = Includes<[boolean, 2, 3, 5, 6, 7], false> // true
We need to use something better to compare. He googled and found this
type test2 = Equal<boolean, false> // false
So now all we need to do is to iterate using our new approach to compare. But first a reminder. With type script we can obtain the first or second element and the rest using the spread operator. This is the same for types
let [first, ...rest] = [1, 2, 3, 4];
let [first,second ...rest] = [1, 2, 3, 4];
So what we do below is we separate the first element in First off from the Rest of the elements. If equal all good, if not call recursively with the rest until there are no more elements. One last thing, the youtuber also thought this was not a simple challenge
type Includes<T extends any[], U> = T extends [infer First, ...infer Rest] ?
(Equal<U, First> extends true ? true : Includes<Rest, U>) : false
Example 9 Parameters
So Given
const foo = (arg1: string, arg2: number): void => {}
const bar = (arg1: boolean, arg2: { a: 'A' }): void => {}
const baz = (): void => {}
const buz = (arg1: number): number => { return 0}
type cases = [
Expect<Equal<MyParameters<typeof foo>, [string, number]>>,
Expect<Equal<MyParameters<typeof bar>, [boolean, { a: 'A' }]>>,
Expect<Equal<MyParameters<typeof baz>, []>>,
Expect<Equal<MyParameters<typeof buz>, [number]>>,
]
// Solve
type MyParameters<T extends (...args: any[]) => any> = any
So we are passing (): void => {} so we should be able to capture the input as (...args: T) => any
type MyParameters<T> = T extends ( (...args: infer U) => any ) ? U : never
Example 9 Parameters
So Given
const foo = (arg1: string, arg2: number): void => {}
const bar = (arg1: boolean, arg2: { a: 'A' }): void => {}
const baz = (): void => {}
const buz = (arg1: number): number => { return 0}
type cases = [
Expect<Equal<MyParameters<typeof foo>, [string, number]>>,
Expect<Equal<MyParameters<typeof bar>, [boolean, { a: 'A' }]>>,
Expect<Equal<MyParameters<typeof baz>, []>>,
Expect<Equal<MyParameters<typeof buz>, [number]>>,
]
// Solve
type MyParameters<T extends (...args: any[]) => any> = any
So we are passing (): void => {} so we should be able to capture the input as (...args: T) => any
type MyParameters<T> = T extends ( (...args: infer U) => any ) ? U : never
Example 10 Return Value
Possibly getting easier. Copying is getting quicker and now I used my learning today so not a waste.
const fn = (v: boolean) => {
if (v)
return 1
else
return 2
}
type MyReturnType<T> = any
type cases = [
Expect<Equal<string, MyReturnType<() => string>>>,
Expect<Equal<123, MyReturnType<() => 123>>>,
Expect<Equal<ComplexObject, MyReturnType<() => ComplexObject>>>,
Expect<Equal<Promise<boolean>, MyReturnType<() => Promise<boolean>>>>,
Expect<Equal<() => 'foo', MyReturnType<() => () => 'foo'>>>,
Expect<Equal<1 | 2, MyReturnType<typeof fn>>>,
Expect<Equal<1 | 2, MyReturnType<typeof fn1>>>,
]
If we look at the examples for the test cases and switching them around we see a pattern
MyReturnType<() => string
MyReturnType<() => 123
MyReturnType<() => Promise<boolean>
MyReturnType<() => () => 'foo'
MyReturnType<typeof fn>
MyReturnType<typeof fn1>
The last two look slightly different but they are not. typeof fn just means
fn() => number
fn1() => number
So the answer is
type MyReturnType<T extends Function> =
T extends (...args: any) => infer R
? R
: never
Example 11 MyReadonly2
interface Todo1 {
title: string
description?: string
completed: boolean
}
interface Todo2 {
readonly title: string
description?: string
completed: boolean
}
interface Expected {
readonly title: string
readonly description?: string
completed: boolean
}
Expect<Alike<MyReadonly2<Todo1>, Readonly<Todo1>>>,
Expect<Alike<MyReadonly2<Todo1, 'title' | 'description'>, Expected>>,
Expect<Alike<MyReadonly2<Todo2, 'title' | 'description'>, Expected>>,
Expect<Alike<MyReadonly2<Todo2, 'description' >, Expected>>,
type MyReadonly2<T, K> = any
This was though. The first thing to notice is the first test case. For this we need to remember we can have default arguments in typescript
type MyReadonly2<T, K extends keyof T = keyof T> = any
Next we need to understand intersection which is the & sign. This will combine the sets in the braces.
type MyReadonly2<T, K extends keyof T = keyof T> =
{ SET A } &
{ SET B }
Set A is all of the keys specified.
type MyReadonly2<T, K extends keyof T = keyof T> =
{ readonly [P in K] : T[P] } &
{ SET B }
Set B is just excluding the value which are not in K. This is the same as the Omit. Understand the Mapped As and this is easy
type MyReadonly2<T, K extends keyof T = keyof T> =
{ readonly [P in K] : T[P] } &
{ [P in keyof T as P extends K ? never : P]: T[P] }
Example 12 Deep Readonly and Recursion
This was going so well as I understood recursion. Simply add read only to the object
type X1 = {
a: () => 22
b: string
c: {
d: boolean
e: {
g: {
h: {
i: true
j: 'string'
}
k: 'hello'
}
l: [
'hi',
{
m: ['hey']
},
]
}
}
}
type Expected1 = {
readonly a: () => 22
readonly b: string
readonly c: {
readonly d: boolean
readonly e: {
readonly g: {
readonly h: {
readonly i: true
readonly j: 'string'
}
readonly k: 'hello'
}
readonly l: readonly [
'hi',
{
readonly m: readonly ['hey']
},
]
}
}
}
So all we have to do it tell if it is an object or keyof
type DeepReadonly<T> = keyof T extends never
? T // Object
: { readonly [k in keyof T]: DeepReadonly<T[k]> }; // Not an Object
Which was great until this example where the type is a union
type X2 = { a: string } | { b: number }
type Expected2 = { readonly a: string } | { readonly b: number }