Hello, I'm Mateusz Roth. Versatile Software Engineer from 🇵🇱🇪🇺, specialized in JavaScript, TypeScript, Node.js, React. I'm eager to work with Node.js, Python, Golang, serverless or IoT.
Open-minded, friendly person that loves learning.

Objective C programming language attributes

Wypisywanie w konsoli:

1
2
NSLog(@"%@", strValue);
NSLog(flag ? @"Yes" : @"No");
  • For Strings you use %@
  • For int you use %i
  • For float and double you use %f
  • For bool you use %d

Consty i zmienne:

1
2
const char NEWLINE = '\n';
int area;

Tworzenie klas i obiektów:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@interface SampleClass:NSObject
- (void)sampleMethod;
@end

@implementation SampleClass
- (void)sampleMethod {
NSLog(@"Hello, World! \n");
}
@end

int main() {
/* my first program in Objective-C */
SampleClass *sampleClass = [[SampleClass alloc]init];
[sampleClass sampleMethod];
return 0;
}

Tworzenie struct:

1
2
3
4
5
6
struct Books {
NSString *title;
NSString *author;
NSString *subject;
int book_id;
};

Manipulowanie struct:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@interface BookLogger:NSObject
/* function declaration */
- (void) printBook:(struct Books*) book;
@end

@implementation BookLogger
- (void) printBook:(struct Books*) book {
NSLog(@"Book title: %@\n", book->title);
NSLog(@"Book author: %@\n", book->author);
NSLog(@"Book subject: %@\n", book->subject);
NSLog(@"Book book_id: %d\n", book->book_id);
}
@end

int main() {
struct Books Book1; /* Declare Book1 of type Book */
struct Books Book2; /* Declare Book2 of type Book */

BookLogger *bookLogger = [[BookLogger alloc]init];
/* print Book1 info */
[sampleClass bookLogger: Book1];
}

Tworzenie bloków funkcji:

1
2
3
4
5
- (return_type) method_name:(argumentType1)argumentName1 
joiningArgument2:(argumentType2)argumentName2 ...
joiningArgumentn:(argumentTypen)argumentNamen {
body of the function
}

i.e.:

1
2
3
4
5
6
7
double (^multiplyTwoValues)(double, double) = 
^(double firstValue, double secondValue) {
return firstValue * secondValue;
};

double result = multiplyTwoValues(2,4);
NSLog(@"The result is %f", result);

Struktury danych:

  • NSDictionary could be accessed like dict[@"key"] or [dict objectForKey:@"key"]
  • NSArray is accessible via indexes: 0, 1, 2 etc:

Inicjacja NSArray:

1
2
NSArray* array = @[@"One", @"Two", @"Three"];
NSArray* array = @[Object1, Object2]

Iterowanie po NSDictionary:

1
2
3
4
5
for (NSString* key in yourDict) {
NSLog(@"%@", yourDict[key]);
//or
NSLog(@"%@", [yourDict objectForKey:key]);
}

Inicjalizacja NSDictionary:

1
NSDictionary* dict = @{ key : value, key2 : value2};

Dodawanie do NSDictionary wartości niebędającej obiektem wymaga jej rzutowania na odpowiedni obiekt Objective C:

1
2
@"facebookId": [NSNumber numberWithInt:[fbId intValue]];
@"facebookId": @(fbId) // for non pointer datatype value store in Dictionary

@property:

atomic, nonatomic, etc.

@synthesize:

@synthesize

Frontend - printing documents

Print iframe

JS:

1
2
window.frames["printf"].focus();
window.frames["printf"].print();

HTML:

1
<iframe id="printf" name="printf"></iframe>

JS:

1
document.getElementById("printf").contentWindow.print()​​​​​​;

More: https://stackoverflow.com/questions/9616426/javascript-print-iframe-contents-only

Print div

1
2
3
4
5
6
7
8
9
10
function printDiv(divName) {
var printContents = document.getElementById(divName).innerHTML;
var originalContents = document.body.innerHTML;

document.body.innerHTML = printContents;

window.print();

document.body.innerHTML = originalContents;
}

jspdf nie nadaje się do robienia plików HTML

More: https://cdn.rawgit.com/MrRio/jsPDF/master/examples/html2pdf/showcase_supported_html.html

My implementation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import replace from "lodash/replace";
import { getDataBase64String, MIME_HTML } from "../constants/mimeTypes";

export const REPLACE_SCRIPT = `
<script>
window.loadPrint = function loadPrint() {
window.print();
setTimeout(function () { window.close(); document.clear(); document.close(); }, 100);
}

window.onload = function() { window.loadPrint(); }
</script>
`;
const HTML_TAG = "</html>";
const HTML_TAG_CAPITALIZED = "</HTML>";

function printHtmlDocument(dataBase64Source, iframeElement) {
const dataBase64Prefix = getDataBase64String(MIME_HTML);
const base64Source = dataBase64Source.slice(dataBase64Prefix.length);
const source = atob(base64Source);

const hasHtmlTag = source.indexOf(HTML_TAG) > -1;
const hasCapitalizedHtmlTag = source.indexOf(HTML_TAG_CAPITALIZED) > -1;

let processedHtml;

if (hasHtmlTag && !hasCapitalizedHtmlTag) {
processedHtml = replace(source, HTML_TAG, REPLACE_SCRIPT + HTML_TAG);
} else if (!hasHtmlTag && hasCapitalizedHtmlTag) {
processedHtml = replace(
source,
HTML_TAG_CAPITALIZED,
REPLACE_SCRIPT + HTML_TAG_CAPITALIZED
);
}

if (!processedHtml) {
processedHtml = source + REPLACE_SCRIPT;
}

const processedBase64Source = btoa(processedHtml);
const processedDataBase64Source = dataBase64Prefix + processedBase64Source;

const iframe = iframeElement || document.createElement("iframe");
iframe.src = processedDataBase64Source;
iframe.height = "0";
iframe.width = "0";
iframe.title = "";
document.body.appendChild(iframe);
setTimeout(() => iframe.remove(), 1000);

return {
html: processedHtml,
dataBase64: processedDataBase64Source,
iframe: iframe,
};
}

export default printHtmlDocument;

Tests

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import printHtmlDocument, { REPLACE_SCRIPT } from "./printHtmlDocument";
import { DATA_MIME_HTML_BASE64 } from "../constants/mimeTypes";

describe("printHtmlDocument", () => {
const HTML = "<html><body>Hello</body></html>";
const HTML_EXPECTED = `<html><body>Hello</body>${REPLACE_SCRIPT}</html>`;
const SOURCE = DATA_MIME_HTML_BASE64 + btoa(HTML);
const SOURCE_EXPECTED = DATA_MIME_HTML_BASE64 + btoa(HTML_EXPECTED);

test("it should return properly modified HTML with included REPLACE_SCRIPT script", () => {
expect(printHtmlDocument(SOURCE).html).toEqual(HTML_EXPECTED);
});

test("it should return properly modified BASE64 source", () => {
expect(printHtmlDocument(SOURCE).dataBase64).toEqual(SOURCE_EXPECTED);
});

test("it should create iframe element with proper attributes", () => {
const result = printHtmlDocument(SOURCE);
expect(result.iframe.src).toEqual(SOURCE_EXPECTED);
expect(result.iframe.height).toEqual("0");
expect(result.iframe.width).toEqual("0");
expect(result.iframe.title).toEqual("");
});
});

Cordova overview & plugins

Debugging

At that time, when I needed to find a bug on Android, I had to build the web app, build the Android app with Cordova, launch it on a device or an emulator, launch chrome dev tool to connect to the running app, put my breakpoints, find a possible cause, fix it in my code and do this process all over again. This whole process was taking way too much time.

1
2
3
$ cordova platform add <platform>
$ cordova platform remove <platform>
$ cordova requirements <platform>

iOS emulators list

1
$ cordova emulate ios --list

To emulate iOS 12+

1
2
$ cordova run ios --emulator --target="iPad-Air-2, 12.1" --buildFlag="-UseModernBuildSystem=0"
// -l --livereload doesn't work

Run on a device

1
$ cordova run android --device

Use emulator with VSCode and live reload (Ionic)

https://geeklearning.io/live-debug-your-cordova-ionic-application-with-visual-studio-code/

iOS

https://developer.apple.com/download/more/
https://download.developer.apple.com/Developer_Tools/Xcode_9.4.1/Xcode_9.4.1.xip

Android

Cordova Android

https://cordova.apache.org/docs/en/latest/guide/platforms/android/#setting-environment-variables

How to create AVD

https://developer.android.com/studio/run/managing-avds#viewing

Android Emulator

https://developer.android.com/studio/run/emulator#about

Start the emulator from the command line

https://developer.android.com/studio/run/emulator-commandline

Threading

Threading in Android Java:

http://burnignorance.com/phonegap-tips-and-tricks/implementing-multi-threading-in-android-plugin-for-phonegap-2-6-0/

1
2
3
4
5
6
- (void) create
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self saySomething];
});
}

Cordova plugin Android threading

https://stackoverflow.com/a/28980819

Threading Cordova plugin iOS

https://cordova.apache.org/docs/en/latest/guide/platforms/ios/plugin.html#threading

iOS and Android difference

1
2
Plugin methods ordinarily execute in the same thread as the main interface. 
https://cordova.apache.org/docs/en/latest/guide/platforms/ios/plugin.html#threading

but on Android:

1
2
The plugin's JavaScript does not run in the main thread of the WebView interface; instead, it runs on the WebCore thread, as does the execute method.
https://cordova.apache.org/docs/en/latest/guide/platforms/android/plugin.html#threading

CSS - How to make child div wider than parent div

By using calc

Source

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
html,
.parent {
height: 100%;
width: 100%;
text-align: center;
padding: 0;
margin: 0;
}
.parent {
width: 50%;
max-width: 800px;
background: grey;
margin: 0 auto;
position: relative;
}
.child-element {
position: relative;
width: 100vw;
left: calc(-50vw + 50%);
height: 50px;
background: blue;
}

How do we make a full-browser-width container when we’re inside a limited-width parent?

Source

1
2
3
4
5
6
7
8
.full-width {
width: 100vw;
position: relative;
left: 50%;
right: 50%;
margin-left: -50vw;
margin-right: -50vw;
}

GraphQL Apollo links

  • self-documentation - you build your types
  • types information available to the client coder through introspection with tools like GraphiQL
  • specification which forms a standardization
  • make everything go through an API gateway which would usually do the auth resolution for all microservices

GraphCMS:
https://docs.graphcms.com/
https://github.com/GraphCMS/graphcms-examples/blob/master/current/next-apollo-blog/lib/initApollo.js

GraphQL example:
https://nec.is/writing/graphql-with-next-js-and-apollo/

Next, Apollo, Postgraphile:
https://github.com/graphile/postgraphile-apollo-server

Next, Apollo, Graphcool:
https://github.com/adamsoffer/next-apollo-example
https://github.com/zeit/next.js/tree/master/examples/with-apollo

Next & Apollo:
https://github.com/lfades/next-with-apollo
https://github.com/adamsoffer/next-apollo

Next, Express, Apollo:
https://github.com/ooade/next-apollo-auth

Hasura and Apollo:
https://medium.com/@f71uday/building-production-ready-application-with-hasura-and-apollo-476b18ccb44

Hasura vs Postgraphile:
https://news.ycombinator.com/item?id=17540866

Postgraphile:
https://www.graphile.org/postgraphile/

Hasura - GraphQL on Postgres:
https://hasura.io/

Graphcool - GraphQL BaaS:
https://www.graph.cool/

Prisma - GrapQL on any database:
https://www.prisma.io/with-graphql/

Nextjs, Apollo, Prisma:
https://github.com/mbaranovski/nextjs-apollo-oauth-prisma

RAN! React . GraphQL . Next.js Toolkit - bez backendu, przykład z Graphcool:
https://github.com/Sly777/ran
https://github.com/mshameer/ran-with-prisma

Vulcan - React, GraphQL, Meteor:
http://docs.vulcanjs.org/

Reason programming language attributes

How to start React project in Reason:

Variants

Variants in Reason are data types and structures. It can be used to define sets of symbols and data structures.
type animal = Cat(string) | Dog(string);

Functions

Functions in Reason are declared with an arrow and a return expression.

1
2
3
4
5
let speak = (animal) =>
switch (animal) {
| Cat(name) => name ++ " says: meow"
| Dog(name) => name ++ " says: woof"
};

React

A React component can either be stateless or stateful. In ReasonReact, there are different ways of defining them:

  • Stateless components: defined as ReasonReact.statelessComponent("componentName")
  • Stateful components: defined as ReasonReact.reducerComponent("componentName")

let make = (~message, _children) => {
The first parameter has a symbol ~ indicating that it was passed into the App component as a props and the second parameter has _, this is a more explicit way of showing that the parameter isn’t used and ignored.

Software Engineering - various terms

Terms

function declaration vs definition, parameter type, return type

Empty variables interpretation

  • null means nothing - we want no value
  • undefined declared but not defined - we want default value

coupling (miara współzależności)

In software engineering, coupling is the degree of interdependence between software modules; a measure of how closely connected two routines or modules are; the strength of the relationships between modules.

Coupling is usually contrasted with cohesion. Low coupling often correlates with high cohesion, and vice versa. Low coupling is often a sign of a well-structured computer system and a good design, and when combined with high cohesion, supports the general goals of high readability and maintainability.

cohesion (miara spójności)

In computer programming, cohesion refers to the degree to which the elements inside a module belong together. In one sense, it is a measure of the strength of relationship between the methods and data of a class and some unifying purpose or concept served by that class. In another sense, it is a measure of the strength of relationship between the class’s methods and data themselves.

Cohesion is an ordinal type of measurement and is usually described as “high cohesion” or “low cohesion”. Modules with high cohesion tend to be preferable, because high cohesion is associated with several desirable traits of software including robustness, reliability, reusability, and understandability. In contrast, low cohesion is associated with undesirable traits such as being difficult to maintain, test, reuse, or even understand.

loose coupling

In computing and systems design a loosely coupled system is one in which each of its components has, or makes use of, little or no knowledge of the definitions of other separate components. Subareas include the coupling of classes, interfaces, data, and services. Loose coupling is the opposite of tight coupling.

big ball of mud

A big ball of mud is a software system that lacks a perceivable architecture. Although undesirable from a software engineering point of view, such systems are common in practice due to business pressures, developer turnover and code entropy. They are a type of design anti-pattern.

software entropy

The second law of thermodynamics, in principle, states that a closed system’s disorder cannot be reduced, it can only remain unchanged or increase. A measure of this disorder is entropy. This law also seems plausible for software systems; as a system is modified, its disorder, or entropy, tends to increase. This is known as software entropy.

The process of code refactoring can result in stepwise reductions in software entropy.

Software rot, also known as code rot, software erosion, software decay or software entropy is either a slow deterioration of software performance over time or its diminishing responsiveness that will eventually lead to software becoming faulty, unusable, or in need of upgrade.

feature creep

Feature creep is the excessive ongoing expansion or addition of new features in a product,[1] especially in computer software, videogames and consumer and business electronics. These extra features go beyond the basic function of the product and can result in software bloat and over-complication, rather than simple design.

software brittleness

In computer programming and software engineering, software brittleness is the increased difficulty in fixing older software that may appear reliable, but fails badly when presented with unusual data or altered in a seemingly minor way.

first-class functions

In computer science, a programming language is said to have first-class functions if it treats functions as first-class citizens. This means the language supports passing functions as arguments to other functions, returning them as the values from other functions, and assigning them to variables or storing them in data structures.[1] Some programming language theorists require support for anonymous functions (function literals) as well.[2] In languages with first-class functions, the names of functions do not have any special status; they are treated like ordinary variables with a function type.[3]
First-class functions are a necessity for the functional programming style, in which the use of higher-order functions is a standard practice. A simple example of a higher-ordered function is the map function, which takes, as its arguments, a function and a list, and returns the list formed by applying the function to each member of the list. For a language to support map, it must support passing a function as an argument.
Source https://en.m.wikipedia.org/wiki/First-class_function

higher order function

a function taking another function as argument is called a higher-order function

Authorization vs Authentication

Authentication describes the process of claiming an identity. That’s what you do when you log in to a service with a username and password, you authenticate yourself.
Authorization on the other hand describes permission rules that specify the access rights of individual users and user groups to certain parts of the system.

Monkey patching

https://en.wikipedia.org/wiki/Monkey_patch
https://www.audero.it/blog/2016/12/05/monkey-patching-javascript/

Domain-driven design (skrót DDD)

  • podejście do tworzenia oprogramowania kładące nacisk na takie definiowanie obiektów i komponentów systemu oraz ich zachowań, aby wiernie odzwierciedlały rzeczywistość. Dopiero po utworzeniu takiego modelu należy rozważyć zagadnienia związane z techniczną realizacją. Podejście to umożliwia modelowanie systemów informatycznych przez ekspertów, którzy znają specyfikę problemu, lecz nie muszą znać się na projektowaniu architektury systemów informatycznych.

Command–query separation (CQS)

  • is a principle of imperative computer programming. It was devised by Bertrand Meyer as part of his pioneering work on the Eiffel programming language. It states that every method should either be a command that performs an action, or a query that returns data to the caller, but not both. In other words, Asking a question should not change the answer.[1] More formally, methods should return a value only if they are referentially transparent and hence possess no side effects.

Design Patterns

Design Patterns

List of design patterns
Source #1 - Common Design Patterns for Android with Kotlin

Creational patterns

They define how you create objects.

  • Builder
  • Dependency Injection - explained below
  • Singleton

Structural patterns

They define how you compose objects.

  • Adapter - this pattern lets two incompatible classes work together by converting the interface of a class into another interface the client expect
  • Facade - The Facade pattern provides a higher-level interface that makes a set of other interfaces easier to use -> for example a repository in Android project can be a facade
  • DAO - a design pattern for writing objects that access data
    • You should use DAOs. The pattern lends itself to modularized code. You keep all your persistence logic in one place (separation of concerns, fight leaky abstractions). You allow yourself to test data access separately from the rest of the application. And you allow yourself to test the rest of the application isolated from data access (i.e. you can mock your DAOs).

Behavioral patterns

They define how you coordinate object interactions.

  • Command - Similarly, the Command pattern lets you issue requests without knowing the receiver. You encapsulate a request as an object and send it off; deciding how to complete the request is an entirely separate mechanism.
  • Observer - The Observer pattern defines a one-to-many dependency between objects. When one object changes state, all of its dependents are notified and updated automatically.
  • Model View Controller
  • Model View ViewModel - The ViewModel object is the “glue” between the model and view layers, but operates differently than the Controller component. Instead, it exposes commands for the view and binds the view to the model. When the model updates, the corresponding views update as well via the data binding. Similarly, as the user interacts with the view, the bindings work in the opposite direction to automatically update the model. This reactive pattern removes a lot of glue code.

Dependency Injection & Dependency Inversion principle explained

1
2
3
class Parent {
private val child = Child()
}

A Parent instance creates its child field when it’s instantiated. The Parent instance is dependent on the concrete implementation of Child and on configuring the child field to use it.

This presents a coupling or dependency of the Parent class on the Child class. If the setup of a Child object is complex, all that complexity will be reflected within the Parent class as well. You will need to edit Parent to configure a Child object.

If the Child class itself depends on a class C, which in turn depends on class D, then all that complexity will propagate throughout the code base and hence result in a tight coupling between the components of the application.

Dependency Injection is the term used to describe the technique of loosening the coupling just described. In the simple example above, only one tiny change is needed:

1
class Parent(private val child: Child)

Voilà — that’s dependency injection at its core!

There is also Dependency Inversion principle. The gist of the Dependency Inversion principle is that it is important to depend on abstractions rather than concrete implementations. In the simple example above, this means changing Child to a Kotlin interface rather than a Kotlin class. With this change, many different types of concrete Child type objects that adhere to the Child interface can be passed into the Parent constructor.

Source - Dependency Injection in Android with Dagger 2 and Kotlin

TypeScript basics

! operator

1
if (a!.b!.c) { }

compiles to JS:

1
2
if (a.b.c) {
}

? operator

1
let x = foo?.bar.baz();

compiles to JS:

1
let x = foo === null || foo === undefined ? undefined : foo.bar.baz();

And:

1
2
3
if (someObj?.bar) {
// ...
}

is equivalent in JS to:

1
2
3
if (someObj &amp;&amp; someObj.someProperty) {
// ...
}

TS:

1
2
3
4
5
6
7
8
9
10
interface Content {
getUrl?: () => string;
url?: string;
}
interface Data {
content?: Content;
}
let data: Data | undefined;
const url: string | undefined = data?.content?.getUrl?.();
const url2: string | undefined = data?.content?.url;

TypeScript with React

Common Operators and Signatures

Type intersection operator (&)

1
class WithLoading extends React.Component<P & WithLoadingProps> { ... }

Generic function

1
const funcComponent = <P extends object>(Component: React.ComponentType<P>): ... => { ... }

Type cast

A type cast (props as P) is required when passing props forward from TypeScript v3.2 onwards, due to a likely bug in TypeScript.

1
return loading ? <LoadingSpinner /> : <Component {...props as P} />;

Examples

Worth to read:

Higher Order Components

Higher Order Component in JS

1
2
3
4
5
6
7
const withLoading = Component =>
class WithLoading extends React.Component {
render() {
const { loading, ...props } = this.props;
return loading ? <LoadingSpinner /> : <Component {...props} />;
}
};

Higher Order Component in TS

1
2
3
4
5
6
7
8
9
10
11
interface WithLoadingProps {
loading: boolean;
}

const withLoading = <P extends object>(Component: React.ComponentType<P>) =>
class WithLoading extends React.Component<P & WithLoadingProps> {
render() {
const { loading, ...props } = this.props;
return loading ? <LoadingSpinner /> : <Component {...props as P} />;
}
};

Function Higher Order Component in TS

1
2
3
4
5
6
7
const withLoading = <P extends object>(
Component: React.ComponentType<P>
): React.FC<P & WithLoadingProps> => ({
loading,
...props
}: WithLoadingProps) =>
loading ? <LoadingSpinner /> : <Component {...props as P} />;

Render Prop Component

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
interface InjectedCounterProps {
value: number;
onIncrement(): void;
onDecrement(): void;
}

interface MakeCounterProps {
minValue?: number;
maxValue?: number;
children(props: InjectedCounterProps): JSX.Element;
}

interface MakeCounterState {
value: number;
}

class MakeCounter extends React.Component<MakeCounterProps, MakeCounterState> {
state: MakeCounterState = {
value: 0,
};

increment = () => {
this.setState(prevState => ({
value:
prevState.value === this.props.maxValue
? prevState.value
: prevState.value + 1,
}));
};

decrement = () => {
this.setState(prevState => ({
value:
prevState.value === this.props.minValue
? prevState.value
: prevState.value - 1,
}));
};

render() {
return this.props.children({
value: this.state.value,
onIncrement: this.increment,
onDecrement: this.decrement,
});
}
}

make-counter-render-prop.tsx

Usage

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
interface CounterProps extends InjectedCounterProps {
style: React.CSSProperties;
}

const Counter = (props: CounterProps) => (
<div style={props.style}>
<button onClick={props.onDecrement}> - </button>
{props.value}
<button onClick={props.onIncrement}> + </button>
</div>
);

interface WrappedCounterProps extends CounterProps {
minValue?: number;
maxValue?: number;
}

const WrappedCounter = ({
minValue,
maxValue,
...props
}: WrappedCounterProps) => (
<MakeCounter minValue={minValue} maxValue={maxValue}>
{injectedProps => <Counter {...props} {...injectedProps} />}
</MakeCounter>
);

wrapped-counter.tsx

Wrapping Render Prop as HOC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { Subtract, Omit } from 'utility-types';
import MakeCounter, { MakeCounterProps, InjectedCounterProps } from './MakeCounter';

type MakeCounterHocProps = Omit<MakeCounterProps, 'children'>;

const makeCounter = <P extends InjectedCounterProps>(
Component: React.ComponentType<P>
): React.SFC<Subtract<P, InjectedCounterProps> & MakeCounterHocProps> => ({
minValue,
maxValue,
...props
}: MakeCounterHocProps) => (
<MakeCounter minValue={minValue} maxValue={maxValue}>
{injectedProps => <Component {...props as P} {...injectedProps} />}
</MakeCounter>
);

Source: https://medium.com/@jrwebdev/react-render-props-in-typescript-b561b00bc67c

React Hooks

Function Components with Hooks in TS example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// import useState next to FunctionComponent
import React, { FunctionComponent, useState } from "react";

// our components props accept a number for the initial value
const Counter: FunctionComponent<{ initial?: number }> = ({ initial = 0 }) => {
// since we pass a number here, clicks is going to be a number.
// setClicks is a function that accepts either a number or a function returning
// a number
const [clicks, setClicks] = useState(initial);
return (
<>
<p>Clicks: {clicks}</p>
<button onClick={() => setClicks(clicks + 1)}>+</button>
<button onClick={() => setClicks(clicks - 1)}>-</button>
</>
);
};

Details about React Hooks with TypeScript

1
2
3
const Counter:FunctionComponent<{ initial?: number }> = ({ initial = 0 }) => {
...
}

with useState

1
2
3
4
5
6
7
8
9
// explicitly setting the types
const [value, setValue] = (useState < number) | (undefined > undefined);
const [value, setValue] = useState < Array < number >> [];

interface MyObject {
foo: string;
bar?: number;
}
const [value, setValue] = useState <MyObject> { foo: "hello" };

with useRef

1
const inputEl = useRef <HTMLInputElement> null;

with useContext

1
2
type Theme = "light" | "dark";
const ThemeContext = createContext <Theme> "dark";

with useReducer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
interface State {
value: number;
}

type Action =
| { type: "increment" }
| { type: "decrement" }
| { type: "incrementAmount", amount: number };

const counterReducer = (state: State, action: Action) => {
switch (action.type) {
case "increment":
return { value: state.value + 1 };
case "decrement":
return { value: state.value - 1 };
case "incrementAmount":
return { value: state.value + action.amount };
default:
throw new Error();
}
};

const [state, dispatch] = useReducer(counterReducer, { value: 0 });

dispatch({ type: "increment" });
dispatch({ type: "decrement" });
dispatch({ type: "incrementAmount", amount: 10 });

// TypeScript compilation error
dispatch({ type: "invalidActionType" });