Refine existing notes on TS
This commit is contained in:
parent
d2cdadc856
commit
e3d7296f8b
8 changed files with 261 additions and 278 deletions
|
@ -5,9 +5,10 @@ tags:
|
|||
---
|
||||
|
||||
# Classes
|
||||
|
||||
## Type declarations for classes
|
||||
|
||||
TypeScript offers full type annotations for classes. It also introduces several TypeScript-specific options (control access modifiers, interfaces etc) that do not exist in JavaScript but which that seek to bring it into closer alignment with more strict object-oriented languages like Java and C#.
|
||||
TypeScript offers full type annotations for classes. It also introduces several TypeScript-specific options (control access modifiers, interfaces etc) that do not exist in JavaScript but which seek to bring it into closer alignment with more strict object-oriented languages like Java and C#.
|
||||
|
||||
A class in JavaScript:
|
||||
|
||||
|
@ -23,9 +24,9 @@ class Age {
|
|||
get age() {
|
||||
return this.currentYear() - this.birthYear;
|
||||
}
|
||||
get dataOutput(){
|
||||
return `${this.personName} is ${this.age} years old`;
|
||||
}
|
||||
get dataOutput() {
|
||||
return `${this.personName} is ${this.age} years old`;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -33,7 +34,7 @@ The same class in TypeScript:
|
|||
|
||||
```ts
|
||||
class Age {
|
||||
personName: string;
|
||||
personName: string;
|
||||
birthYear: number;
|
||||
constructor(personName: string, birthYear: number) {
|
||||
this.personName = personName;
|
||||
|
@ -53,9 +54,9 @@ class Age {
|
|||
|
||||
The main points to note are:
|
||||
|
||||
* methods must specify their return type, as with [functions](Functions.md)
|
||||
* the constructor function must specify its parameters' types
|
||||
* we must declare the types of any properties we intend to use at the start of the class.
|
||||
- methods must specify their return type, as with [functions](Functions.md)
|
||||
- the constructor function must specify its parameters' types
|
||||
- we must declare the types of any properties we intend to use at the start of the class.
|
||||
|
||||
### Instantiating a class
|
||||
|
||||
|
@ -66,12 +67,12 @@ const mum = new Age('Mary Jo', 1959);
|
|||
console.log(mum);
|
||||
|
||||
/* Age { personName: 'Mary Jo', birthYear: 1959 } */
|
||||
````
|
||||
```
|
||||
|
||||
But given that classes define objects, we can also now use `Age` as a new custom type and define an object that way
|
||||
|
||||
```jsx
|
||||
const thomas: Age = new Age('Thomas', 1988);
|
||||
const thomas: Age = new Age('Thomas', 1988);
|
||||
```
|
||||
|
||||
### Without constructor
|
||||
|
@ -97,10 +98,10 @@ Let's say we have the following interface:
|
|||
|
||||
```ts
|
||||
interface Person {
|
||||
firstName: string,
|
||||
secondName: string,
|
||||
age: number,
|
||||
employed: () => boolean
|
||||
firstName: string;
|
||||
secondName: string;
|
||||
age: number;
|
||||
employed: () => boolean;
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -108,7 +109,7 @@ Now we want to create a class that must share this shape. We go ahead and create
|
|||
|
||||
```ts
|
||||
class Programmer implements Person {
|
||||
// If the below are not included, TS will generate an error
|
||||
// If the below are not included, TS will generate an error
|
||||
firstName: string,
|
||||
secondName: string,
|
||||
age: number,
|
||||
|
|
158
Programming_Languages/TypeScript/Custom_types.md
Normal file
158
Programming_Languages/TypeScript/Custom_types.md
Normal file
|
@ -0,0 +1,158 @@
|
|||
---
|
||||
tags:
|
||||
- Programming_Languages
|
||||
- typescript
|
||||
---
|
||||
|
||||
# Custom types
|
||||
|
||||
Objects and [classes](./Classes.md) are where TypeScript becomes most useful and powerful. In TypeScript, objects and classes are by definition custom types.
|
||||
|
||||
When typing objects, you do not write the types alongside the actual data as you would with primitive types, classes and functions. Instead you write a custom type which is a type-annotated object. This is then applied to the object or class, where you create instances that **match the shape** of the custom declaration.
|
||||
|
||||
So say we have this object:
|
||||
|
||||
```js
|
||||
const age = {
|
||||
name: 'Thomas',
|
||||
yearOfBirth: 1988,
|
||||
currentYear: 2021,
|
||||
ageNow: function () {
|
||||
return this.currentYear - this.yearOfBirth;
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
We could write this as type with the following:
|
||||
|
||||
```ts
|
||||
let Age: {
|
||||
name: string;
|
||||
yearOfBirth: number;
|
||||
currentYear: number;
|
||||
ageNow: () => number;
|
||||
};
|
||||
```
|
||||
|
||||
(We use `:` because we are declaring a type not intialising a value of the type.)
|
||||
|
||||
We could now re-write the first `age` object as an object of type `Age` :
|
||||
|
||||
```tsx
|
||||
let thomas: typeof Age;
|
||||
|
||||
thomas = {
|
||||
name: 'Thomas',
|
||||
yearOfBirth: 1988,
|
||||
currentYear: 2021,
|
||||
ageNow: function () {
|
||||
return this.currentYear - this.yearOfBirth;
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
In practice, defining the type and then asserting that a new variable is of this type and then initialising it is rather long-winded. It is better practice to simplify the process by creating a **type alias**.
|
||||
|
||||
```tsx
|
||||
type Age = {
|
||||
name: string;
|
||||
yearOfBirth: number;
|
||||
currentYear: number;
|
||||
ageNow(): number; // we type the method on the basis of the value it returns
|
||||
};
|
||||
```
|
||||
|
||||
We could then create objects based on this:
|
||||
|
||||
```tsx
|
||||
const thomas: Age = {
|
||||
name: 'Thomas',
|
||||
yearOfBirth: 1988,
|
||||
currentYear: 2021,
|
||||
ageNow: function () {
|
||||
return this.currentYear - this.yearOfBirth;
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
Note that we pass in `:Age` as our type declaration, using the custom type in the same way as we would use `:string` or `number[]` . We can now use this custom type as a type annotation anywhere we use type annotations; it can be used exactly the same way as a primitive type in our code.
|
||||
|
||||
`Age` is an alias for the type that `thomas` conforms to.
|
||||
|
||||
The benefit is that TS will correct you if:
|
||||
|
||||
- attempt to assign a type to a value that does not match the custom type declaration (for instance: assigning a string value to a property you have typed as number)
|
||||
- attempt to add a property that is not specified in the custom type declaration
|
||||
|
||||
Although you can subsequently _extend_ the custom type (detailed below).
|
||||
|
||||
## Duck typing
|
||||
|
||||
> Types are defined by the collection of their properties not their name.
|
||||
|
||||
Typescript's implementation of types is as a **structural type system**, which contrasts with a nominal type system. This is often referred to colloquially as 'duck typing': _if it looks like a duck, walks like a duck, and sounds like a duck, it probably is a duck_.
|
||||
|
||||
With custom (object types) this means that the following expression of an object of type `Age` doesn't generate an error, TS is satisfied that the shapes of each match.
|
||||
|
||||
```tsx
|
||||
const martha = {
|
||||
name: 'Martha',
|
||||
yearOfBirth: 1997,
|
||||
currentYear: 2021,
|
||||
gender: 'female',
|
||||
};
|
||||
|
||||
const addition: Age = martha;
|
||||
```
|
||||
|
||||
But if we tried to add this extra property whilst defining `martha` as an instance of the custom type `Age` , we would get an error:
|
||||
|
||||
```tsx
|
||||
const martha: Age = {
|
||||
name: 'Martha',
|
||||
yearOfBirth: 1997,
|
||||
currentYear: 2021,
|
||||
gender: 'female',
|
||||
};
|
||||
```
|
||||
|
||||
```
|
||||
Type '{ name: string; yearOfBirth: number; currentYear: number; ageNow: () => number; gender: string; }' is not assignable to type 'Age'. **Object literal may only specify known properties, and 'gender' does not exist in type 'Age'.**
|
||||
```
|
||||
|
||||
It means that even though in the following, the variable `point` is never declared to be of the type `Point` , it matches the shape of the custom type. As the structural integrity is maintained, it can be passed to the function without error.
|
||||
|
||||
```tsx
|
||||
interface Point {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
function logPoint(p: Point) {
|
||||
console.log(`${p.x}, ${p.y}`);
|
||||
}
|
||||
|
||||
// logs "12, 26"
|
||||
const point = {x: 12, y: 26};
|
||||
logPoint(point);
|
||||
```
|
||||
|
||||
Shape matching only requires a subset of the object's fields to match:
|
||||
|
||||
```tsx
|
||||
const point3 = {x: 12, y: 26, z: 89};
|
||||
logPoint(point3); // logs "12, 26"
|
||||
|
||||
const rect = {x: 33, y: 3, width: 30, height: 80};
|
||||
logPoint(rect);
|
||||
```
|
||||
|
||||
## Interfaces
|
||||
|
||||
For most purposes the keywords `type` and `interface` are interchangeable in TypeScript.
|
||||
|
||||
(The main difference that I have encountered is that Angular specifies that interfaces should be used over types, but this is because it is class-based and object oriented).
|
||||
|
||||
For me, the main decider is that Angular favours `interface` over `type`. The TS handbook recommends using the `type` keyword over `interface`.
|
||||
|
||||
An interface is concept that crosses over from strict OOP and it doesn't quite apply to interfaces in TS because an interface is a declaration of a class's functions alone, not its data (i.e properties).
|
|
@ -22,25 +22,20 @@ function search(query: string, tags?: string[]): string {}
|
|||
|
||||
### Utilising custom types
|
||||
|
||||
|
||||
Whilst we can use standard JS types with the parameters and return value, the real benefit comes when you use custom types. For instance we can specify that an object passed to a function must match the shape of a custom type or interface. Similarly we can ensure that for functions that return objects, the object that is returned must satisfy the shape of the custom object.
|
||||
|
||||
```ts
|
||||
async function getContributorData(
|
||||
contributorName: string
|
||||
): Promise<IContributor> {}
|
||||
async function getContributorData(contributorName: string): Promise<IContributor> {}
|
||||
```
|
||||
|
||||
For example, this function has a return signature which indicates that it will return a promise matching a type of shape `IContributor`
|
||||
|
||||
## Typing functions themselves
|
||||
|
||||
---
|
||||
## Functions as types
|
||||
|
||||
As well as typing the values that a function receives and returns, you can type the function itself. **This is most useful when you are using higher-order functions and passing functions as parameters to another function.** In these scenarios you will want to type the function that is being passed as a parameter. There are several ways to do this. We'll use the following basic function as our demonstration:
|
||||
|
||||
```ts
|
||||
function hoFunc(integer: number, addFunction: any): number {
|
||||
function higherOrderFunction(integer: number, addFunction: any): number {
|
||||
return addFunction(integer);
|
||||
}
|
||||
```
|
||||
|
@ -52,11 +47,11 @@ function hoFunc(integer: number, addFunction: any): number {
|
|||
const addTwo = (int: number) => int + 2;
|
||||
|
||||
// Apply it:
|
||||
hoFunc(3, addTwo);
|
||||
higherOrderFunction(3, addTwo);
|
||||
|
||||
// We can now define the higher-order function with a specific type:
|
||||
|
||||
function hoFunc(integer: number, addFunction: typeof addTwo): number {
|
||||
function higherOrderFunction(integer: number, addFunction: typeof addTwo): number {
|
||||
return addFunction(integer);
|
||||
}
|
||||
```
|
||||
|
|
36
Programming_Languages/TypeScript/Primitive_types.md
Normal file
36
Programming_Languages/TypeScript/Primitive_types.md
Normal file
|
@ -0,0 +1,36 @@
|
|||
---
|
||||
tags:
|
||||
- Programming_Languages
|
||||
- typescript
|
||||
- data_types
|
||||
---
|
||||
|
||||
# Primitive types
|
||||
|
||||
The most basic type level is the one already present in JavaScript itself: the primitive data types: `boolean`, `string` and `number` . These are the types that would be returned from a `typeof` expression in ordinary JavaScript
|
||||
|
||||
## Type inference
|
||||
|
||||
You can explicitly declare these data types yourself when you create a variable with `var`, `const`, or `let` if you like, but it is generally unnecessary since Typescript is intelligent enough to perform **type inference** and will know from what you write which type you mean.
|
||||
|
||||
```ts
|
||||
const age: number = 32;
|
||||
const name: string = 'Thomas';
|
||||
const one: boolean = true;
|
||||
const zero: boolean = false;
|
||||
```
|
||||
|
||||
## Arrays
|
||||
|
||||
With arrays that you directly define the type will also be inferred but it can be helpful to explicitly declare them. If you declare an empty array, it is necessary to assign empty brackets.
|
||||
|
||||
```ts
|
||||
const store: number[] = [1, 2, 3]; // Populated array
|
||||
const store: string[] = []; // Empty array
|
||||
```
|
||||
|
||||
## Object
|
||||
|
||||
`Object` is a valid type declaration in TS but it is not particularly helpful since it becomes similar to using [any](./Any.md) given that most primitive types in JavaScripts prototypically inherit from an Object.
|
||||
|
||||
Generally, when you use objects in TypeScript you type them as [custom types](./Custom_types.md).
|
|
@ -4,92 +4,92 @@ tags:
|
|||
- typescript
|
||||
---
|
||||
|
||||
# Setup and configuration
|
||||
|
||||
## Installation and configuration
|
||||
|
||||
---
|
||||
|
||||
TypeScript offers custom installations for most modern JS-based frameworks including Webpack, React.js and Vue.js. The instructions below cover minimal TS set-up outside of a specific framework. TypeScript adds an additional step to any build process since your code must compile to JS before any bundling and transpilation can occur. This is why using the custom installations helps to de-complicate things.
|
||||
TypeScript offers custom installations for most modern JS-based frameworks including Webpack, React.js and Vue.js. The instructions below cover minimal TS set-up outside of a specific framework. TypeScript adds an additional step to any build process since your code must compile to JS before any bundling and transpilation can occur.
|
||||
|
||||
## Installation
|
||||
|
||||
````
|
||||
```
|
||||
mkdir typescript-project
|
||||
cd typescript-project
|
||||
npm i typescript --save-dev
|
||||
````
|
||||
```
|
||||
|
||||
## Initialise project
|
||||
|
||||
````
|
||||
```
|
||||
npx tsc --init
|
||||
````
|
||||
```
|
||||
|
||||
## Basic configuration
|
||||
|
||||
````json
|
||||
```json
|
||||
"compilerOptions": {
|
||||
"target" : "es2020", //es2015 for prod
|
||||
"module" : "es2020",
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"strict": true,
|
||||
"esModuleInterop": true
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"strict": true,
|
||||
"esModuleInterop": true
|
||||
}
|
||||
````
|
||||
```
|
||||
|
||||
## Specify output paths (for production code)
|
||||
### Specify output paths (for production code)
|
||||
|
||||
````json
|
||||
```json
|
||||
"compilerOptions": {
|
||||
...
|
||||
"outDir": "dist",
|
||||
"sourceMap": true,
|
||||
}
|
||||
````
|
||||
```
|
||||
|
||||
## Compile-time commands
|
||||
### Compile-time commands
|
||||
|
||||
````
|
||||
tsc --noEmit
|
||||
````
|
||||
```
|
||||
tsc --noEmit
|
||||
```
|
||||
|
||||
````
|
||||
```
|
||||
tsc --noEmit --watch
|
||||
````
|
||||
```
|
||||
|
||||
## Global requirements
|
||||
|
||||
````
|
||||
```
|
||||
npm install -g typescript
|
||||
npm install -g ts-node
|
||||
````
|
||||
```
|
||||
|
||||
`ts-node` allows you to run TS files the way you would with standard JS, i.e. `node [filename]`, you just use `ts-node filename` .
|
||||
|
||||
This is a convenience that saves you compiling from TS → JS and then running Node against the compiled JS. Which is useful for debugging and checking values as you work.
|
||||
|
||||
# Imports and exports
|
||||
## Imports and exports
|
||||
|
||||
You may wish to define your custom types outside of their application, for instance in a `/types/` directory. The custom type or types can then be imported using standard ES6 imports:
|
||||
|
||||
````tsx
|
||||
```tsx
|
||||
export type Article = {
|
||||
title: string,
|
||||
title: string,
|
||||
price: number,
|
||||
}
|
||||
|
||||
export type AnotherType = {
|
||||
...
|
||||
}
|
||||
````
|
||||
```
|
||||
|
||||
````tsx
|
||||
import { Article, AnotherType } from './types/allTypes.js';
|
||||
```tsx
|
||||
import {Article, AnotherType} from './types/allTypes.js';
|
||||
import Article from './types/allTypes.js';
|
||||
````
|
||||
```
|
||||
|
||||
TypeScript also provides a specific import keyword `type` so that you can distinguish type imports from other module imports. This is handy when you have multiple components in the one file:
|
||||
|
||||
````tsx
|
||||
import type { Article } from "./types/allTypes.js";
|
||||
````
|
||||
```tsx
|
||||
import type {Article} from './types/allTypes.js';
|
||||
```
|
||||
|
|
|
@ -1,217 +0,0 @@
|
|||
---
|
||||
tags:
|
||||
- Programming_Languages
|
||||
- typescript
|
||||
---
|
||||
|
||||
# Type declarations
|
||||
## Scalar data types
|
||||
|
||||
The most basic type level is the one already present in JavaScript itself: the primitive data types: `boolean`, `string` and `number` . These are the types that will be returned from a `typeof` expression.
|
||||
|
||||
You can explicitly declare these data types yourself when you create a variable with `var`, `const`, or `let` if you like, but it is generally unnecessary since Typescript is intelligent enough to perform \*\*type inference \*\*\*\*and will know from what you write which type you mean. However with complex code or code that requires mutations, it may be helpful for your own understanding to explicitly declare the type. The syntax is as follows:
|
||||
|
||||
````tsx
|
||||
const age: number = 32;
|
||||
const name: string = 'Thomas';
|
||||
const one: boolean = true;
|
||||
const zero: boolean = false;
|
||||
````
|
||||
|
||||
## Reference types
|
||||
|
||||
This is where you want to start declaring types explicitly.
|
||||
|
||||
### Arrays
|
||||
|
||||
With arrays that you populate at declaration, the type will be inferred but I think it will be helpful to declare them, to help with debugging. If you declare an unpopulated/empty array, it is necessary to declare the empty brackets.
|
||||
|
||||
````tsx
|
||||
const store: number[] = [1, 2, 3]; // Populated array
|
||||
const store: number[] = [] // Empty array
|
||||
````
|
||||
|
||||
### Objects
|
||||
|
||||
Objects (and classes) are where TypeScript becomes really useful and powerful, especially when you fuse custom types and shape.
|
||||
|
||||
In Typescript you don't really have type annotations for the key pairs of an object. This is to say: you don't declare the types as you write the object. Instead you declare a custom type, which is a type-annotated object, and then create instances of that object which **match the shape** of the custom declaration.
|
||||
|
||||
So say we have this object:
|
||||
|
||||
````jsx
|
||||
const age = {
|
||||
name: 'Thomas',
|
||||
yearOfBirth: 1988,
|
||||
currentYear: 2021,
|
||||
ageNow: function(){
|
||||
return this.currentYear - this.yearOfBirth;
|
||||
}
|
||||
};
|
||||
````
|
||||
|
||||
We could write this as type with the following:
|
||||
|
||||
````tsx
|
||||
let Age : {
|
||||
name: string,
|
||||
yearOfBirth: number
|
||||
currentYear: number,
|
||||
ageNow: () => number
|
||||
}
|
||||
````
|
||||
|
||||
We use `:` because we are declaring a type not intialising a value of the type.
|
||||
|
||||
We could now re-write the first `age` object as an object of type `Age` :
|
||||
|
||||
````tsx
|
||||
let thomas: typeof Age;
|
||||
|
||||
thomas = {
|
||||
name: 'Thomas',
|
||||
yearOfBirth: 1988,
|
||||
currentYear: 2021,
|
||||
ageNow: function () {
|
||||
return this.currentYear - this.yearOfBirth;
|
||||
},
|
||||
};
|
||||
````
|
||||
|
||||
In practise, defining the type and then asserting that a new variable is of this type and then initialising it is rather long-winded. It is better practice to simplify the process by creating a type alias.
|
||||
|
||||
````tsx
|
||||
type Age = {
|
||||
name: string,
|
||||
yearOfBirth: number,
|
||||
currentYear: number,
|
||||
ageNow():number, // we type the method on the basis of the value it returns
|
||||
}
|
||||
````
|
||||
|
||||
We could then create objects based on this:
|
||||
|
||||
````tsx
|
||||
const thomas: Age = {
|
||||
name: 'Thomas',
|
||||
yearOfBirth: 1988,
|
||||
currentYear: 2021,
|
||||
ageNow: function () {
|
||||
return this.currentYear - this.yearOfBirth;
|
||||
},
|
||||
};
|
||||
````
|
||||
|
||||
Note that we pass in `:Age` as our type declaration, using the custom type in the same way as we would use `:string` or `number[]` . We can now use this custom type as a type annotation anywhere we use type annotations, it can be used exactly the same way as a scalar or reference type in our code.
|
||||
|
||||
Note that when we do this we are using a **type alias**. `Age` is an alias for the type that `thomas` conforms to.
|
||||
|
||||
The benefit is that TS will correct you if:
|
||||
|
||||
* attempt to assign a type to a value that does not match the custom type declaration (for instance: assigning a string value to a property you have typed as number)
|
||||
* attempt to add a property that is not specified in the custom type declaration
|
||||
|
||||
Although you can subsequently extend the custom type (see below)
|
||||
|
||||
### Interlude: object methods
|
||||
|
||||
In our example we include a method in the definition of the custom `Age` type. This is fine but it means that when we create instances of `Age` like `thomas` , we have to constantly rewrite the same method with each new instance:
|
||||
|
||||
````tsx
|
||||
...
|
||||
ageNow: function () {
|
||||
return this.currentYear - this.yearOfBirth;
|
||||
},
|
||||
````
|
||||
|
||||
This is always going to be the same so it violates DRY to write it every time. In these cases it would be better to either use a class (since the method would carry over to each instance of the class) or, if you want to remain within the realm of objects, create a function that takes an `Age` type as a parameter and then applies the method, for instance:
|
||||
|
||||
````tsx
|
||||
function ageNow(person: Age): number {
|
||||
return person.currentYear - person.yearOfBirth;
|
||||
}
|
||||
|
||||
console.log(ageNow(thomas)) // 33
|
||||
````
|
||||
|
||||
See below for more info on functions \[link\] and classes \[link\].
|
||||
|
||||
[Object methods in TypesScript](https://www.reddit.com/r/typescript/comments/m8rck4/object_methods_in_typesscript/)
|
||||
|
||||
For more info, see discussion I started on this on /r/typescript
|
||||
|
||||
### Interlude: duck typing 🦆
|
||||
|
||||
>
|
||||
> Types are defined by the collection of their properties not their name.
|
||||
|
||||
Typescript's implementation of types is as a **structural type system**, which contrasts with a nominal type system. This is often referred to colloquially as 'duck typing': *if it looks like a duck, walks like a duck, and sounds like a duck, it probably is a duck*.
|
||||
|
||||
With custom (object types) this means that the following expression of an object of type `Age` doesn't generate an error, TS is satisfied that the shapes of each match.
|
||||
|
||||
````tsx
|
||||
const martha = {
|
||||
name: 'Martha',
|
||||
yearOfBirth: 1997,
|
||||
currentYear: 2021,
|
||||
gender: 'female',
|
||||
};
|
||||
|
||||
const addition: Age = martha;
|
||||
````
|
||||
|
||||
But if we tried to add this extra property whilst defining `martha` as an instance of the custom type `Age` , we would get an error:
|
||||
|
||||
````tsx
|
||||
const martha: Age = {
|
||||
name: 'Martha',
|
||||
yearOfBirth: 1997,
|
||||
currentYear: 2021,
|
||||
gender: 'female',
|
||||
};
|
||||
````
|
||||
|
||||
````
|
||||
Type '{ name: string; yearOfBirth: number; currentYear: number; ageNow: () => number; gender: string; }' is not assignable to type 'Age'. **Object literal may only specify known properties, and 'gender' does not exist in type 'Age'.**
|
||||
````
|
||||
|
||||
It means that even though in the following, the variable `point` is never declared to be of the type `Point` , it matches the shape of the custom type. As the structural integrity is maintained, it can be passed to the function without error.
|
||||
|
||||
````tsx
|
||||
interface Point {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
function logPoint(p: Point) {
|
||||
console.log(`${p.x}, ${p.y}`);
|
||||
}
|
||||
|
||||
// logs "12, 26"
|
||||
const point = { x: 12, y: 26 };
|
||||
logPoint(point);
|
||||
````
|
||||
|
||||
Shape matching only requires a subset of the object's fields to match:
|
||||
|
||||
````tsx
|
||||
const point3 = { x: 12, y: 26, z: 89 };
|
||||
logPoint(point3); // logs "12, 26"
|
||||
|
||||
const rect = { x: 33, y: 3, width: 30, height: 80 };
|
||||
logPoint(rect);
|
||||
````
|
||||
|
||||
## Interfaces
|
||||
|
||||
For most purposes the keywords `type` and `interface` are interchangeable. For me, the main decider is that Angular favours `interface` over `type`.
|
||||
|
||||
An interface is concept that crosses over from strict OOP.
|
||||
|
||||
>
|
||||
> In Object Oriented Programming, an Interface is a description of all functions that an object must have in order to be an "X". Again, as an example, anything that "ACTS LIKE" a light, should have a `turn_on()` method and a `turn_off()` method. The purpose of interfaces is to allow the computer to enforce these properties and to know that an object of TYPE T (whatever the interface is ) must have functions called X,Y,Z, etc. **An interface is about actions that are allowed, not about data or implementation of those actions.**
|
||||
|
||||
>
|
||||
> But think also about the real semantics of the word: an interface could be a gear stick, a light switch or a door lock accessed with a key. Interfaces allow an external consumer to interact with a complex system that lies behind the interface. In code, the interface represents the ways to use the capabilities of the object.
|
||||
|
||||
So with standard OOP interfaces concern the functions that an object possesses. We can include function typings in TS interfaces but generally, an interface/type outlines the structure of a JS *object.*
|
|
@ -4,4 +4,8 @@ tags:
|
|||
- typescript
|
||||
---
|
||||
|
||||
Type narrowing is the process of working out from a supertype like any or unknown what type the value should be in the x course of your code. Related to this is type guarding: ensuring that a value is of the suitable type as a factor of control flow. For instance using typeof to ensure that an input is numerical before proceeding with a function's logic.
|
||||
# Type narrowing and guarding
|
||||
|
||||
Type narrowing is the process of working out from a supertype like [any](./Any.md) or [unknown](./Unknown.md) whic type the value should be in the x course of your code. This is necessary since a process will not necessarily always return or involve homogenous types.
|
||||
|
||||
Related to this is type guarding: ensuring that a value is of the suitable type as a factor of control flow. For instance using typeof to ensure that an input is numerical before proceeding with a function's logic.
|
||||
|
|
|
@ -4,4 +4,10 @@ tags:
|
|||
- typescript
|
||||
---
|
||||
|
||||
You might think that a good use case for `any` is a scenario where you don't know in advance what the data type is going to be. In order to mark this, you put `any` there as a placeholder. Actually TS provides a type for this specific purpose: `unknown`. Like `any`, `unknown` is equivalent to every type in TS (it is a supertype) but it is deliberately inhibiting, in contrast to `any`. When you use `unknown` you have to use type narrowing to avoid it being returned. So if your code starts with `unknown`, you should filter out scenarios where it evaluates to each of the possible types otherwise if `unknown` is returned it will cause an error.
|
||||
# Unknown
|
||||
|
||||
You might think that a good use case for `any` is a scenario where you don't know in advance what the data type is going to be.
|
||||
|
||||
In order to mark that this is the case, you type the value as `any`. In fact, TypeScript provides a type for this specific scenario: `unknown`.
|
||||
|
||||
Like `any`, `unknown` is equivalent to every type in TS (it is also a supertype) but it is deliberately inhibiting, in contrast to `any`. When you use `unknown` you have to use type narrowing to avoid it being returned. So if your code starts with `unknown`, you should filter out scenarios where it evaluates to each of the possible types otherwise if `unknown` is returned it will cause an error.
|
||||
|
|
Loading…
Add table
Reference in a new issue