Skip to content

Commit

Permalink
More examples
Browse files Browse the repository at this point in the history
  • Loading branch information
orta committed Aug 26, 2019
1 parent c50b141 commit a1ca06f
Show file tree
Hide file tree
Showing 45 changed files with 449 additions and 11 deletions.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ class Safe {
}
}

const a = new Safe("Crown Jewels")
a.printContents()
const safe = new Safe("Crown Jewels")
safe.printContents()

// If you come form an objected oriented language where the
// this/self variable is easily predictable, then you may
Expand All @@ -32,22 +32,22 @@ a.printContents()
// object, and then call it through that - the this variable
// has moved refer to the hosting object:

const customObjectCapturingThis = { contents: "http://gph.is/VxeHsW", print: a.printContents }
const customObjectCapturingThis = { contents: "http://gph.is/VxeHsW", print: safe.printContents }
customObjectCapturingThis.print() // Prints "http://gph.is/VxeHsW" - not "Crown Jewels"

// This is tricky, because when dealing with callback APIs -
// it can be very tempting to pass the function reference
// directly. This can be worked around by creating a new
// function at the call site.

const objectNotCapturingThis = { contents: "N/A", print: () => a.printContents() }
const objectNotCapturingThis = { contents: "N/A", print: () => safe.printContents() }
objectNotCapturingThis.print()

// There are a few are ways to work around this problem. One
// route is to force the binding of this, to be the object
// you originally intended via bind.

const customObjectCapturingThisAgain = { contents: "N/A", print: a.printContents.bind(a) }
const customObjectCapturingThisAgain = { contents: "N/A", print: safe.printContents.bind(a) }
customObjectCapturingThisAgain.print()

// To work around an unexpected this context, you can also
Expand All @@ -72,12 +72,19 @@ class SafelyBoundSafe {
// Now passing the function to another object
// to run does not accidentally change this

const safeSafe = new SafelyBoundSafe("Golden Skull")
safeSafe.printContents()
const saferSafe = new SafelyBoundSafe("Golden Skull")
saferSafe.printContents()

const customObjectTryingToChangeThis = {
contents: "http://gph.is/XLof62",
print: safeSafe.printContents
print: saferSafe.printContents
}

customObjectTryingToChangeThis.print()

// If you have a TypeScript project, you can use the compiler
// flag noImplicitThis to highlight cases where TypeScript
// cannot determine what type "this" is for a function.
//
// You can learn more about that in the handbook
// https://www.typescriptlang.org/docs/handbook/utility-types.html#thistypet
103 changes: 103 additions & 0 deletions Examples/en/TypeScript/Advanced Meta-Types/Conditional Types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Conditionals Types provide a way to do simple logic in the
// TypeScript type system. This is definitely an advanced
// feature, and it's quite feasible that you won't need to
// use this in your normal day to day code.

// A conditional type looks like:
//
// A extends B ? C : D
//
// Where the conditional is whether a type extends an
// expression, and if so what type should be returned. A
// type could also be deferred for

// Let's go through some simple examples

type Cat = { meows: true }
type Dog = { barks: true }
type Cheetah = { meow: true, fast: true }
type Wolf = { barks: true, howls: true }

// We can create a conditional type which lets use extract
// types which only conform to something which barks.

type ExtractDogish<A> = A extends { barks: true } ? A : never

// Then we can create types which ExtractDogish wraps:

// A cat doesn't bark, so it will return never
type NeverCat = ExtractDogish<Cat>
// A wolf will bark, so it returns the wolf
type Wolfish = ExtractDogish<Wolf>

// This becomes useful when you want to work with a
// union of many types and reduce the number of potential
// options in a union,

type Animals = Cat | Dog | Cheetah | Wolf

// When you apply ExtractDogish to a union type, it is the
// same as running the conditional against each member of
// the type

type Dogish = ExtractDogish<Animals>

// = ExtractDogish<Cat> | ExtractDogish<Dog> |
// ExtractDogish<Ceetah> | ExtractDogish<Wolf>

// = never | Dog | never | Wolf

// = Dog | Wolf (see example:unknown-and-never)

// This is call a distributive conditional type because the
// type distributes over each member of the union.


// Deferred Conditional Types

// Conditional types can be used to tighten your APIs which
// can return different types depending on the inputs.

// For example this function which could return either a
// string or number depending on the boolean passed in.

declare function getUserID<T extends boolean>(user: {}, oldSystem: T): T extends true ? string : number

// Then depending on how much the type-system knows about
// the boolean, you will get different return types:

let stringReturnValue = getUserID({}, /* oldSystem */ true)
let numberReturnValue = getUserID({}, /* oldSystem */ false)
let stringOrID = getUserID({}, /* oldSystem */ Math.random() < 0.5)

// In this case above TypeScript can know the return value
// instantly. However, you can use conditional types in functions
// where the type isn't known yet. This is called a deferred
// conditional type.

// Same as our Dogish above, but as a function instead
declare function isCatish<T>(x: T): T extends { meows: true } ? T : undefined;

// In this Generic function there are no constraints on the
// generic parameter U, and so it could be any type.

// This means using Catish would need to wait to know what
// the actual type of U is before it can be applied.

function findOtherAnimalsOfType<U>(animal: U) {
let maybeCat = isCatish(animal)

// You can use assignment to short-circuit the process
// if you' have a good idea about the types during
// implementation
let b: Cat | Cheetah | undefined = maybeCat;
return maybeCat
}

const a = findOtherAnimalsOfType<Animals>({ meow: true, fast: true })
const b = findOtherAnimalsOfType<Cat>({ meows: true})

// TODO: This example needs work!


// typeof process("foo")
23 changes: 23 additions & 0 deletions Examples/en/TypeScript/Language/Soundness.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Without a background in type theory, you're unlikely
// to be familiar with the idea of a type system being "sound"

// Soundness is the idea that the compiler can make guarantees
// about what types are available at runtime, and not just
// during compilation.

// Building a type system for a language is a trade-off with
// three qualities: Simplicity, Usability and Soundness.

// By aiming to support all JavaScript code, TypeScript
// varies its

// code. While runtime safety is an important requirement,
// usability is another important one.

// One of these usability trade-offs is in having a completely
// sound type-system vs having a type system that maps to real
// world usage of JavaScript



// https://github.com/Microsoft/TypeScript/issues/9825
57 changes: 57 additions & 0 deletions Examples/en/TypeScript/Meta-Types/Mapped Types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Mapped types are a way to creates new types based
// on another type. Effectively a transformational type.

// Common cases for using a mapped type is dealing with
// partial subsets of an existing type. For example
// an API may return an Artist:

interface Artist {
id: number
name: string;
bio: string;
}

// However, if you were to send an update to the API which
// only changes a subset of the Artist then you would
// typically have to create an additional type:

interface ArtistForEdit {
id: number
name?: string
bio?: string
}

// It's very likely that this would get out of sync with
// the Artist above. Mapped types let you create a change
// in an existing type.

type MyPartialType<Type> = {
// For every existing property inside the type of Type
// convert it to be a ?: version
[Property in keyof Type]?: Type[Property];
}

// Now we can use the mapped type instead to create
// our edit interface:
type MappedArtistForEdit = MyPartialType<Artist>

// This is close to perfect, but it does allow id to be null
// which should never happen. So, let's make one quick
// improvement by using an intersection type (see:
// example:union-and-intersection-types )

type MyPartialTypeForEdit<Type> = {
[Property in keyof Type]?: Type[Property];
} & { id: number }

// This takes the partial result of the mapped type, and
// merges it with an object which has id: number set.
// Effectively forcing id to be in the type.

type CorrectMappedArtistForEdit = MyPartialTypeForEdit<Artist>

// This is a pretty simple example of how mapped type
// work, but covers most of the basics. If you'd like to
// dive in with more depth, check out the hand-book
//
// https://www.typescriptlang.org/docs/handbook/advanced-types.html#mapped-types
33 changes: 33 additions & 0 deletions Examples/en/TypeScript/Primitives/Any.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Any is the TypeScript escape clause. You can use any to
// either declare a section of your code to be dynamic and
// JavaScript like, or to work around limitations in the
// type system.

// A good case for any is JSON parsing:

const myObject = JSON.parse("{}")

// Any declares to TypeScript to trust your code as being
// safe because you know more about it. Even if that is
// not strictly true. For example, this code would crash:

myObject.x.y.z

// Using an any gives you the ability to write code closer to
// original JavaScript with the trade off of type safety.

// Any is considered a top type in type theory, which means
// that all other objects (except never) can be classed as
// an any - this makes it a good case for functions which
// have a very open allowance for input.

declare function debug(value: any)

debug("a string")
debug(23)
debug({ color: "blue" })

// Unknown is a sibling type to any, if any is about saying
// "I know what's best", then unknown is a way to say "I'm
// not sure what is best, so you need to tell TS the type"
// example:unknown-and-never
69 changes: 69 additions & 0 deletions Examples/en/TypeScript/Primitives/Literals.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// TypeScript has some fun special cases for literals in
// source code.

// In part, a lot of the support is covered in type widening
// and narrowing ( example:type-widening-narrowing ) and it's
// worth covering that first.

// A literal is a more concrete subtype of a collective type.
// What this means is that "Hello World" is a string, but a
// string is not "Hello World" inside the type system

const helloWorld = "Hello World"
let hiWorld = "Hi World" // this is a string because it is let

// This function takes all strings
declare function allowsAnyString(arg: string)
allowsAnyString(helloWorld)
allowsAnyString(hiWorld)

// This function only accepts the string literal "Hello World"
declare function allowsOnlyHello(arg: "Hello World")
allowsOnlyHello(helloWorld)
allowsOnlyHello(hiWorld)

// This lets you declare APIs which use unions to say it
// only accepts a particular literal:

declare function allowsFirstFiveNumbers(arg: 1 | 2 | 3 | 4 | 5)
allowsFirstFiveNumbers(1)
allowsFirstFiveNumbers(10)

let potentiallyAnyNumber = 3
allowsFirstFiveNumbers(potentiallyAnyNumber)

// At first glance, this rule isn't applied to complex objects

const myUser = {
name: "Sabrina"
}

// See how it transforms name: "Sabrina" to name: string?
// even though it is defined as a constant. This is because
// the name can still change any time:

myUser.name = "Cynthia"

// Because myUser's name property can change, TypeScript
// cannot use the literal version in the type system. There
// is a feature which will allow you to do this however.

const myUnchangingUser = {
name: "Fatma"
} as const

// When "as const" is applied to the object, then it becomes
// a object literal which doesn't change instead of a
// mutable object which can.

myUnchangingUser.name = "Raîssa"

// "as const" is a great tool for fixtured data, and places
// where you treat code as literals inline. As const also
// works with arrays

const exampleUsers = [
{ name: "Brian" },
{ name: "Fahrooq" }
] as const

Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ myUserAccount.name
const neverReturns = () => {
// If it throws on the first line
throw new Error("Always throws, never returns")

}

// If you hover on the type, you see it is a () => never
Expand All @@ -68,7 +67,9 @@ const validateUser = (user: User) => {
}

// According to the type system, this
// codepath can never happen.
// codepath can never happen. This is because
// the two nevers match

return neverReturns()
}

Expand Down Expand Up @@ -107,3 +108,17 @@ const flowerLatinName = (flower: Flower) => {

// You will get a compiler error saying that your new
// flower type cannot be converted into never.


// Never in Unions

// A never is something which is automatically removed from
// a type union.

type NeverIsRemoved = string | never | number

// If you look at the type for NeverIsRemoved, you see that
// it is string | number. This is because it should never
// happen at runtime because you cannot assign to it.

// This feature is used a lot in example:conditional-types
Loading

0 comments on commit a1ca06f

Please sign in to comment.