path | title |
---|---|
/learnings/javascript_typescript |
Learnings: Javascript: Typescript |
- >
- Declaring Types
- Variable Declarations
- Declaring your own types
- When that variable is a higher order function
- Creating a typedef / interface for your callback functions
- Promises and types
- Declaring variables read only (not just
const
) - Declaring variables null - or not
- "But this could be a string OR null ...."
- with the strictNullChecks tsconfig parameter turned on
- With the strictNullChecks tsconfig parameter turned off
- So, with strictNullChecks on.... string | null | undefined everywhere???
- Function Declarations
- Optional Parameters
- Generic Types
- Union types
- Declaring Type Types
- Examples of places where we generate types
- Enforcing compile time null checking >
- Declaring Types
- fancy type things
- swicth statment completion
- Interesting additions to JS
- Javascript -> Typescript
- Interacting with Javascript
- Type Checking > Clever Lint Rules
- Book Recommendations
TypeScript is Micosoft' transpiler that adds type checking and attempts to bring new features from ECMAScipt proposals faster than the ECMAScript release cycle can.
Typescript' design goals ane enumerated on the Typescript Github.
function concat( a: string, b: number): string {
return `${a} ${b}`
}
console.log(concat("a", "b"))
console.log( concat(42, {"name": "Ryan"}) ) // will not compile
Like Flow, which does this more drastically, Typescript splits the language into two:
- type space <-- where the types live
- variable space <-- your transpiled code
Type space declarations are not reflected in the running of you program. There's a couple places where keywords implement things in both type and variable space (class
being a prime example).
But this also make things hard, especially in certain situations when you want to know the type at runtime.
let thing: string = 'John Doe'
But sometimes types are infered
You can think of it NOT as "this variable is of this type", but "this variable should match this declaration, or conform to this series of duck-types.
You could think of an object being described by a shape, not an imstance of a type.
- TODO: write this. Both inline types and part of their own thing types.
funtion sort(compareFn: (a: string, b: number) => number): this
interface ConcatCallable {
(error: Error | null, result: string, firstInput: string): void
}
function concat( a: string, b?: string, callback: ConcatCallable): void {
return callback(null, `${a} ${b}`, a)
}
- TODO: write me
- TODO: write me
- Learning_Flow_Typed_Promises
You might think you can use the readonly
attribute, but this only applies to arrays and tuple types. Instead you have to / should use a generic
class Animal {
public name: string = "default"
}
let thing: Readonly<Animal> = new Animal()
thing.name = "Ryan" // compile error here!!
(also readonly
only applies to the array, not deep: it does not mean elements in the array are readonly
!! Cast these to ReadOnly<>
).
But note Readonly<>
is not itself deep either.... you can use a DeepReadonly
generic in ts-essentials
to do this.
- TODO: write me
See also: Learning_Javascript_Typescript_Null_Checking
Note this is not a deep null check...
<<Learning_Javascript_Typescript_Handling_Null_Checking>>
There are no exceptions
With it turned off the following is legal. (With it turned on it is a compiler error):
class Person {
firstName: string
lastName: string
constructor(first: string, last?: string) {
this.firstName = first
this.lastName = last
// type of lastName = string | undefined
// but if strict type checking is off, that is ignored
}
}
new Person('shaq')
- Learning_Javascript_Typescript_Null_Checking
- Learning_Typescript_Null_Handling_Strategies
You have some code that looks like this: let a: string | null | undefined
What you almost want is a Java Optional type, or something to make less typing here.
This is not baked into the Typescript language
- TODO: implementation of Maybe type here?
- TODO: read Functional Thinking Chapter on Functional Data Structures
function sayHello(name: string): string {
return `hello, ${name}'
}
function sayHello(name: string='Ryan') {
return `hello, ${name}'
}
If it's at the end you can use the optional punctuation:
function setName(firstName: string, lastName?: string) {}
However the compiler will not let you put that in anything but last parameter. (even if strict type checking is off).
In the case of an optional parameter in the middle (what ARE you doing???) you will need to do function setName(firtName: string, middleName: string|null, lastName: string) {}
let v: Arry<Honkable> = []
let thing: string | number = 42
but note: If we have a value that has a union type, we can only access members that are common to all types in the union.
- [BOOKQUOTE]: Pros of this approach
would it make sense to have type: 'fill' but then a LineLayout and PointPaint? Certainly not. Convert Layer to a union of interfaces to exclude this possibility:
-
from: Effective Typescript
-
[BOOKQUOTE]: Cons of this approach
Interfaces with multiple properties that are union types are often a mistake because they obscure the relationships between these properties.
-
from: Effective Typescript
-
From: Effective Typescript
can declare a type that is just a value
type AB = 'AB'
- TODO: write me
Mostly like ES6, except
interface Honkable {
honk(times: integer)
}
class Goose implments Honkable {
honk(times: integer) {
console.log('HONK!')
}
}
You can mark fields as private, protected, public, like so
But private is a feature of the type system and, like all features of the type system, it goes away at runtime (Effective Typescript)
--strictNullChecks means you will need to union everything with null if you want to allow it to be null.
- Learning_Javascript_Typescript_Handling_Null_Checking
$ npx type-coverage
let a = inHuman as Being
a as unknown as Goose // <-- go all the way down to the base type, then back up. But seriously, only do this if there's no other alternative
could also be written as a as any as Goose
But what if your function takes a union type, and it needs to call functions impleented in one type or another depending on the action type?
function noise(animal: Goose | Duck) {
if (animal as Goose) {
animal.honk()
} else {
animal.quack()
}
}
You can safely perform this check with a type predicate
Typescript alo has something similar to Learning_Javascript_Refinements
function noise(animal: Goose | Duck) {
if ("quack" in animal) {
animal.quack()
}
- Learning_Typescript_Null_Handling_Predicate_Explaination
- Effective Typescript Chapter 3 Item 22 calls this "type narrowing"
use the never type in the default case to man=ke sure you implemented all of switch
All this typing sometimes gets annoying, particularly when you have a lot of types that are kind sort of but not really like each other. Typescript is here for you
type CheckingAccount {
accountNumber: number,
checks: Array<Check>
balance: number
}
type SavingsAccount = Pick<CheckingAccount, 'accountNumber', 'balanace'>
- Function overloading / multiple type definitions per function <-- can do this but it looks odd
- Access modifiers
public
,protected
,private
- (Java style) interfaces
Can check to see if your JSDocs actually declare types correctly, or if they fail in some way
Q: But this doesn't include type information?? A: if you have type information in jsdoc, yes it appears so
- TODO: check out this statement....
You likely need the type here before going very far. Couple different ways to do this:
- Just cast it.
foobar as Thinger
. May have to pass through any type or something. - Use a user defined type guard (see Learning_Typescript_Type_Guards) and have that cast the variable to the correct type
- Use something like json-schema to generate TS classes from JSON Schema, as described in an atomicobject blog post
- You can use Learning_Typescript_Null_Handling_Predicate_Explaination and
if field in object
checking the various shapes of the instance in quesion... until you find the right one, which by then the compiler should have refined it enogh to know what you are talking about.
in tsconfig.json
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs"
}
}
- TODO: write me
Typesript compiler will error if you call a private method. (This, however, is only enforced at the compiler level, JS don't care).
But I want to unit test private methods, sometimes.
Easiest answer? typecast the object to any ((personObject as any).aVeryPrivateMethod()
)
- Effective Typescript <-- Effective Java/C++ style book