node_modules
dist
# Advanced Types in TypeScript

Coming soon!
<!DOCTYPE html>
<html>
  <body>
    <!-- // make console.log will write to the page for better in-browser experience -->
    <script>
      (function () {
    var body = document.querySelector('body');
    body.style['fontFamily'] = 'monospace';
    body.style['fontSize'] = '2em';
    console.log = function (x) { body.innerText += x + '\n'; };
    }());
  </script>
  <script src="index.js"></script>
  </body>
</html>
//string literal types

let autoComplete: "on" | "off" = "on";
autoComplete = "off";
// string literal types are case sensitive
// the following code will throw an error
// [ts] Type 'ON' is not assignable to type 'autoComplete'
autoComplete = "ON";

// ============================================================
// numeric literal types

type NumberBase = 2 | 8 | 10 | 16;
let base: NumberBase;
base = 2;
// [ts] Type '3' is not assignable to type 'NumberBase'
base = 3;

// ============================================================
// boolean literal types

let autoFocus: true = true;
// [ts] Type 'false' is not assignable to type 'true'
autoFocus = false;

// ============================================================
// enum literal type

enum Protocols {
    HTTP,
    HTTPS,
    FTP
}

type HyperTextProtocol = Protocols.HTTP | Protocols.HTTPS;

let protocol: HyperTextProtocol;
protocol = Protocols.HTTP;
protocol = Protocols.HTTPS;
// [ts] Type 'Protocols.FTP' is not assignable to type 'HyperTextProtocol'
protocol = Protocols.FTP;
function trimAndLower(text: string | null | undefined) {
    if (typeof text === "string") {
        return text.trim().toLowerCase();
    }
    return text;
}

console.log(trimAndLower(" LearnTypeScript.io "));
console.log(trimAndLower(null));
console.log(trimAndLower(undefined));

// ========================================

const container = document.getElementById("app-container")!;
container.remove();
{
    "compilerOptions": {
        "target": "es5",
        "rootDir": "src",
        "outDir": "dist",
        "strictNullChecks": true
    }
}
function trimAndLower(text: string | null | undefined) {
    text;

    if (typeof text === "string") {
        text;

        return text.trim().toLowerCase();
    }

    text;

    return text;
}
{
    "compilerOptions": {
        "target": "es5",
        "rootDir": "src",
        "outDir": "dist",
        "strictNullChecks": true
    }
}
const numbers = [0, 1, 2, [3, 4], 5, [6], [7], 8, [9]];

function isFlat<T>(array: (T | T[])[]): array is T[] {
    return !array.some(Array.isArray);
}

if (isFlat(numbers)) {
    numbers;
}
{
    "compilerOptions": {
        "target": "es5",
        "rootDir": "src",
        "outDir": "dist",
        "strictNullChecks": true
    }
}
const weekdays: ReadonlyArray<string> = [
    "Sunday",
    "Monday",
    "Tuesday",
    "Wednesday",
    "Thursday",
    "Friday",
    "Saturday"
];
{
    "compilerOptions": {
        "target": "es5",
        "rootDir": "src",
        "outDir": "dist",
        "strictNullChecks": true
    }
}
const obj: { [key: string]: string } = {};
obj.hasOwnProperty("foo");
obj.foo = "bar";
{
    "compilerOptions": {
        "target": "es5",
        "rootDir": "src",
        "outDir": "dist",
        "strictNullChecks": true
    }
}
enum ShirtSize {
    S,
    M,
    L,
    XL
}

function assertNever(value: never): never {
    throw Error(`Unexpected value '${value}'`);
}

function prettyPrint(size: ShirtSize) {
    switch (size) {
        case ShirtSize.S: return "small";
        case ShirtSize.M: return "medium";
        case ShirtSize.L: return "large";
        case ShirtSize.XL: return "extra large";
        default: return assertNever(size);
    }
}
{
    "compilerOptions": {
        "target": "es5",
        "rootDir": "src",
        "outDir": "dist",
        "strictNullChecks": true
    }
}
/**
 * Reverses the given string.
 * @param string The string to reverse.
 */
function reverse(string: string): string;

/**
 * Reverses the given array.
 * @param array The array to reverse.
 */
function reverse<T>(array: T[]): T[];

function reverse(stringOrArray: string | any[]) {
    return typeof stringOrArray === "string"
        ? [...stringOrArray].reverse().join("")
        : stringOrArray.slice().reverse();
}

const reversedString = reverse("TypeScript");
const reversedArray = reverse([4, 8, 15, 16, 23, 42]);
{
    "compilerOptions": {
        "target": "es2015",
        "rootDir": "src",
        "outDir": "dist",
        "strictNullChecks": true
    }
}
const enum MediaTypes {
    JSON = "application/json"
}

fetch("https://example.com/api/endpoint", {
    headers: {
        Accept: MediaTypes.JSON
    }
}).then(response => {
    // ...
});
{
    "compilerOptions": {
        "target": "es5",
        "rootDir": "src",
        "outDir": "dist",
        "strictNullChecks": true,
        "preserveConstEnums": true
    }
}
let autoComplete: "on" | "off" = "on";
autoComplete = "off";
autoComplete = "ON";

// ============================================================

type NumberBase = 2 | 8 | 10 | 16;
let base: NumberBase;
base = 2;
base = 3;

// ============================================================

let autoFocus: true = true;
autoFocus = false;

// ============================================================

enum Protocols {
    HTTP,
    HTTPS,
    FTP
}

type HyperTextProtocol = Protocols.HTTP | Protocols.HTTPS;

let protocol: HyperTextProtocol;
protocol = Protocols.HTTP;
protocol = Protocols.HTTPS;
protocol = Protocols.FTP;
{
    "compilerOptions": {
        "target": "es5",
        "rootDir": "src",
        "outDir": "dist",
        "strictNullChecks": true
    }
}
type Result<T> =
    | { success: true; value: T }
    | { success: false; error: string };

function tryParseInt(text: string): Result<number> {
    if (/^-?\d+$/.test(text)) {
        return {
            success: true,
            value: parseInt(text, 10)
        };
    }
    return {
        success: false,
        error: "Invalid number format"
    };
}

const result = tryParseInt("42");

if (result.success) {
    result;
} else {
    result;
}

// ============================================================

interface Cash {
    kind: "cash";
}

interface PayPal {
    kind: "paypal";
    email: string;
}

interface CreditCard {
    kind: "creditcard";
    cardNumber: string;
    securityCode: string;
}

type PaymentMethod = Cash | PayPal | CreditCard;

function stringifyPaymentMethod(method: PaymentMethod): string {
    switch (method.kind) {
        case "cash":
            return "Cash";
        case "paypal":
            return `PayPal (${method.email})`;
        case "creditcard":
            return "Credit Card";
    }
}
{
    "compilerOptions": {
        "target": "es5",
        "rootDir": "src",
        "outDir": "dist",
        "strictNullChecks": true
    }
}
const person = {
    fullName: "Marius Schulz",
    blog: "https://blog.mariusschulz.com",
    twitter: "@mariusschulz"
};

const { fullName, ...socialMedia } = person;

fullName;
socialMedia;

// ============================================================

const defaultStyles = {
    fontFamily: "Arial, sans-serif",
    fontWeight: "normal"
};

const userStyles = {
    color: "#111111",
    fontWeight: 700
};

const styles = {
    ...defaultStyles,
    ...userStyles
};

// ============================================================

const todo = {
    text: "Water the flowers",
    completed: false,
    tags: ["garden"]
};

const shallowCopy = { ...todo };
shallowCopy.text = "Mow the lawn";
shallowCopy.tags.push("weekend");

console.log(shallowCopy);
console.log(todo);
{
    "compilerOptions": {
        "target": "es5",
        "rootDir": "src",
        "outDir": "dist",
        "strictNullChecks": true
    }
}
interface Todo {
    id: number;
    text: string;
    completed: boolean;
}

const todo: Todo = {
    id: 1,
    text: "Buy milk",
    completed: false
};

function prop<T, K extends keyof T>(obj: T, key: K): T[K] {
    return obj[key];
}

const id = prop(todo, "id");
const text = prop(todo, "text");
const completed = prop(todo, "completed");
{
    "compilerOptions": {
        "target": "es5",
        "rootDir": "src",
        "outDir": "dist",
        "strictNullChecks": true
    }
}
interface Point {
    x: number;
    y: number;
}

type Nullable<T> = {
    [P in keyof T]: T[P] | null;
};

type Stringify<T> = {
    [P in keyof T]: string;
};

type PartialNullablePoint = Partial<Nullable<Stringify<Point>>>;

let point: PartialNullablePoint;
point = { x: "0", y: "0" };
point = { x: "0" };
point = { x: undefined, y: null };
{
    "compilerOptions": {
        "target": "es5",
        "rootDir": "src",
        "outDir": "dist",
        "strictNullChecks": true
    }
}
//modified for plunker
{
  "compilerOptions": {
      "target": "es5",
      "outDir": "lib",
      "strictNullChecks": true,
      "preserveConstEnums": true
  }
}

// original: 
// {
//   "compilerOptions": {
//       "target": "es5",
//       "rootDir": "src",
//       "outDir": "dist",
//       "strictNullChecks": true
//   }
// }