From 16953bd93694086fe7153dbe80263ffb5e9954c0 Mon Sep 17 00:00:00 2001 From: Mastermindzh Date: Thu, 15 Jul 2021 11:36:18 +0200 Subject: [PATCH] consolidation of various example repos/gists --- .dockerignore | 2 + .editorconfig | 24 ++ .eslintignore | 7 + .eslintrc | 63 ++++++ .gitignore | 39 ++++ .stylelintrc | 60 +++++ README.md | 64 +++++- bash/parsing-command-line-params.sh | 24 ++ bash/remove-parentheses.sh | 17 ++ bash/whiptail.sh | 9 + c/ani2png.c | 127 +++++++++++ javascript/async/README.md | 10 + javascript/async/async.js | 72 ++++++ javascript/async/image.png | Bin 0 -> 38677 bytes .../state/HttpWithStateSettingsService.ts | 71 ++++++ .../tests/redux-promise-middleware.spec.js | 66 ++++++ typescript/geo/radiusToPolygon/helpers.ts | 52 +++++ typescript/geo/radiusToPolygon/index.ts | 55 +++++ typescript/geo/radiusToPolygon/point.ts | 4 + typescript/inheritance-and-interfaces.ts | 213 ++++++++++++++++++ 20 files changed, 978 insertions(+), 1 deletion(-) create mode 100644 .dockerignore create mode 100644 .editorconfig create mode 100644 .eslintignore create mode 100644 .eslintrc create mode 100644 .gitignore create mode 100644 .stylelintrc create mode 100644 bash/parsing-command-line-params.sh create mode 100644 bash/remove-parentheses.sh create mode 100644 bash/whiptail.sh create mode 100644 c/ani2png.c create mode 100644 javascript/async/README.md create mode 100644 javascript/async/async.js create mode 100644 javascript/async/image.png create mode 100644 js-ts-frameworks/Angular/state/HttpWithStateSettingsService.ts create mode 100644 js-ts-frameworks/React/tests/redux-promise-middleware.spec.js create mode 100644 typescript/geo/radiusToPolygon/helpers.ts create mode 100644 typescript/geo/radiusToPolygon/index.ts create mode 100644 typescript/geo/radiusToPolygon/point.ts create mode 100644 typescript/inheritance-and-interfaces.ts diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..a61c7f7 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +.data +.data/** diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..df19346 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,24 @@ +root = true + +[*] +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +indent_style = space +indent_size = 2 +charset = utf-8 + +[Makefile] +indent_style = tab +indent_size = 8 + +[{deps,tools,build}/**] +indent_style = ignore +indent_size = ignore +end_of_line = ignore +trim_trailing_whitespace = ignore +charset = ignore + +[{test/fixtures,deps,build,tools/eslint,tools/gyp,tools/icu,tools/msvs}/**] +insert_final_newline = false diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..158e0db --- /dev/null +++ b/.eslintignore @@ -0,0 +1,7 @@ +*.scss +*.d.ts +*.spec.ts +main.browser.ts +idex.js +node_modules/* +*.e2e.* diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..c540bbd --- /dev/null +++ b/.eslintrc @@ -0,0 +1,63 @@ +{ + "env": { + "browser": true, + "jest": true, + "es6": true + }, + "plugins": ["import"], + "extends": ["eslint:recommended", "prettier"], + "parserOptions": { + "ecmaVersion": 2020, + "sourceType": "module" + }, + "rules": { + "no-console": [ + "error", + { + "allow": ["debug", "error"] + } + ], + "no-eval": "error", + "import/first": "error", + "camelcase": [ + "error", + { + "ignoreImports": true, + "ignoreDestructuring": true + } + ], + "consistent-return": "warn", + "comma-dangle": ["warn", "always-multiline"], + "constructor-super": "error", + "curly": "error", + "eol-last": "warn", + "eqeqeq": ["error", "smart"], + "import/order": "always", + "new-parens": "error", + "no-debugger": "error", + "no-fallthrough": "off", + "max-len": [ + "warn", + { + "code": 100 + } + ], + "no-shadow": [ + "error", + { + "hoist": "all" + } + ], + "no-trailing-spaces": "warn", + "no-underscore-dangle": "error", + "no-unsafe-finally": "error", + "no-var": "error", + "object-shorthand": "error", + "one-var": ["error", "never"], + "prefer-arrow/prefer-arrow-functions": "off", + "prefer-const": "error", + "radix": "off", + "space-in-parens": ["off", "never"] + } + } + \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..baa0c1a --- /dev/null +++ b/.gitignore @@ -0,0 +1,39 @@ +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +publish/ +build/Release +coverage +lib-cov + +# deps +node_modules/ +__pycache__/ + +# Logs +logs +*.log* + +# Runtime data +pids +*.pid +*.seed + +# Mac files +.DS_Store + +# randoms +*.pyc +dbdata +old +yarn-error.log +dist +*/test-results diff --git a/.stylelintrc b/.stylelintrc new file mode 100644 index 0000000..a20e387 --- /dev/null +++ b/.stylelintrc @@ -0,0 +1,60 @@ +{ + "extends": [ + "stylelint-config-standard", + "stylelint-config-recommended-scss", + "stylelint-config-prettier" + ], + "rules": { + "font-family-name-quotes": "always-where-recommended", + "function-url-quotes": [ + "always", + { + "except": ["empty"] + } + ], + "declaration-empty-line-before":[ + "never", + ], + "comment-empty-line-before":[ + "always", + { + "except": ["first-nested"], + "ignore": ["stylelint-commands"], + "ignoreComments": ["/BEGIN/", "/END/", "/ignore/"] + } + ], + "selector-max-compound-selectors": 5, + "color-hex-length": "long", + "selector-pseudo-element-colon-notation": "single", + "selector-attribute-quotes": "always", + "string-quotes": "double", + "max-nesting-depth": 3, + "selector-max-specificity": "0,3,2", + "declaration-no-important": true, + "at-rule-no-vendor-prefix": true, + "media-feature-name-no-vendor-prefix": true, + "property-no-vendor-prefix": true, + "selector-no-vendor-prefix": true, + "value-no-vendor-prefix": true, + "no-empty-source": null, + "selector-class-pattern": "[a-z-]+", + "selector-id-pattern": "[a-z-]+", + "selector-max-id": 0, + "selector-max-universal": 2, + "selector-type-no-unknown": [ + true, + { + "ignore": ["custom-elements", "default-namespace"] + } + ], + "selector-pseudo-element-no-unknown": [ + true, + { + "ignorePseudoElements": ["ng-deep"] + } + ], + "unit-allowed-list": ["px", "%", "em", "rem", "vw", "vh", "deg", "s"], + "max-empty-lines": 2, + "max-line-length": 120 + } +} diff --git a/README.md b/README.md index 5eb6e1e..996f3f9 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,64 @@ -# examples +# Examples + Various examples for various use-cases, mostly helping others :) + +## why in a repo? + +I like to be platform independent. Things like Github gist are nice but tie me to the platform. +Having these things in a repo gives me flexibility. + +It also consolidates all relevant examples in 1 place + +## table of contents + +```sh +. +├── bash +│ ├── parsing-command-line-params.sh +│ ├── remove-parentheses.sh # script to remove parentheses from all files in a directory (recursively) +│ └── whiptail.sh # tiny demo of how whiptail works + +├── c +│ └── ani2png.c # convery .ani files into a sequence of .pngs + +├── javascript +│ └── async # an example of how to use async / await in javascript +│ ├── async.js +│ ├── image.png +│ └── README.md + +├── js-ts-frameworks # JS-TS framework specific examples +│ ├── Angular +│ │ └── state +│ │ └── HttpWithStateSettingsService.ts # both a higher order component and a state settings service example for Angular + ngxs +│ └── React +│ └── tests +│ └── redux-promise-middleware.spec.js # single spec file to validate a lot of redux promise middleware reducers + +├── mongo +│ ├── add-field-with-default-value.js # add a field to an existing collection with a default value +│ └── collectionNames-and-count.js # return a list of collections in a database with the number of documents in them + +├── typescript +│ ├── datetime +│ │ └── getCustomIsoString.ts # function to return a valid ISO string from a given date object + +│ ├── geo +│ │ └── radiusToPolygon # example on how to convert from a radius to a GeoJSON polygon +│ │ ├── helpers.ts +│ │ ├── index.ts +│ │ └── point.ts + +│ ├── typing +│ │ └── omit.ts # utility type "omit" examples +│ └── inheritance-and-interfaces.ts # basic introduction to inheritance and interfaces for front-end devs + +├── LICENSE +└── README.md +``` + +## external examples + +Examples which are either too big to include or not made by me. + +- [Angular smart vs dumb components](https://github.com/Mastermindzh/angular-smart-dumb-component-example) diff --git a/bash/parsing-command-line-params.sh b/bash/parsing-command-line-params.sh new file mode 100644 index 0000000..907ee2d --- /dev/null +++ b/bash/parsing-command-line-params.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# Allow params a b v +# : is used to disable verbosity +while getopts ":abv" opt; do + + # case to handle scenarias + case $opt in + a) + echo "-a passed, Parameter: $OPTARG" + ;; + b) + echo "-b passed, Parameter: $OPTARG" + ;; + v) + echo "-v passed, Parameter: $OPTARG" + ;; + # default case + \?) + echo "Invalid option: -$OPTARG" >&2 + exit 1 + ;; + esac +done diff --git a/bash/remove-parentheses.sh b/bash/remove-parentheses.sh new file mode 100644 index 0000000..e091930 --- /dev/null +++ b/bash/remove-parentheses.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# will recursively remove ( and ) from all files + +for d in /mnt/series/Smallville/*; do + if [ -d "$d" ]; then + echo "" + echo "=> moving to: $d" + cd "$d" + for f in *.*; do + echo "processing: $f" + des=$(echo "$f" | tr -d '()') + if [ "$f" != "$des" ]; then + mv "$f" "$des" + fi + done + fi +done diff --git a/bash/whiptail.sh b/bash/whiptail.sh new file mode 100644 index 0000000..47c168f --- /dev/null +++ b/bash/whiptail.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +Height=$(tput lines) +Width=$(tput cols) +Height=$((Height / 2)) +Width=$(((Width * 2) / 3)) + +whiptail --yesno --title "Hello!" "This is a whiptail window" $Height $Width + diff --git a/c/ani2png.c b/c/ani2png.c new file mode 100644 index 0000000..e767a9c --- /dev/null +++ b/c/ani2png.c @@ -0,0 +1,127 @@ +//build: gcc ani2png.c -o ani2png + +#include +#include +#include + +void ReadFile(char *name); +int teststring(char *buffer, int start); + +int main(int argc, char **argv) +{ + if (argc != 2) + { + fprintf(stderr, "Usage: ani2png /path.ani"); + return 0; + } + ReadFile(*(argv + 1)); + return 0; +} + +int teststring(char *buffer, int start) +{ + if (*(buffer + start) == 0x69) + { + if (*(buffer + start + 1) == 0x63) + { + if (*(buffer + start + 2) == 0x6f) + { + if (*(buffer + start + 3) == 0x6e) + { + return 1; + } + } + } + } + else + return 0; +} + +void ReadFile(char *name) +{ + FILE *file; + char *buffer, *fileName; + unsigned long fileLen; + + if (!strstr(name, ".ani")) + { + fprintf(stderr, "Usage: ani2png /path.ani"); + return; + } + + fileName = malloc(strlen(name) + 1); + strcpy(fileName, name); + *strstr(fileName, ".ani") = '\0'; + + file = fopen(name, "rb"); + + fseek(file, 0, SEEK_END); + fileLen = ftell(file); + fseek(file, 0, SEEK_SET); + + buffer = (char *)malloc(fileLen + 1); + + fread(buffer, fileLen, 1, file); + fclose(file); + + char *new_png_name = malloc(strlen(name) + 5); + + char png_counter_string[5]; + int i, j, png_counter = 1; + FILE *png_image; + + for (i = 0; i <= fileLen; i++) + { + if (png_counter == 9999) + { + free(fileName); + free(buffer); + free(new_png_name); + return; + } + + if ((i + 4 <= fileLen) && teststring(buffer, i) == 1) + { + sprintf(png_counter_string, "%d", png_counter); + strcpy(new_png_name, fileName); + strcat(new_png_name, png_counter_string); + strcat(new_png_name, ".png"); + png_counter++; + + png_image = fopen(new_png_name, "wb"); + if (!png_image) + { + fprintf(stderr, "Unable to open file %s.\n", new_png_name); + free(fileName); + free(buffer); + free(new_png_name); + return; + } + + j = 8; + while (i + j + 4 <= fileLen) + { + if (teststring(buffer, i + j + 1) == 1) + break; + if (j == 10) + putc(0x01, png_image); + else + putc(*(buffer + i + j), png_image); + j++; + } + if (i + j <= fileLen) + putc(*(buffer + i + j), png_image); + if (fileLen - i - j <= 3) + { + putc(*(buffer + i + j + 1), png_image); + putc(*(buffer + i + j + 2), png_image); + } + fclose(png_image); + i += j; + } + } + + free(new_png_name); + free(buffer); + free(fileName); +} diff --git a/javascript/async/README.md b/javascript/async/README.md new file mode 100644 index 0000000..7bd2a49 --- /dev/null +++ b/javascript/async/README.md @@ -0,0 +1,10 @@ +# Async + +Copy to a file (e.g test.js) and run with: +node test.js + +Result should be something like this: + +![image showing the result](./image.png) + +PS: To clarify. Both loops run at the same time, to test remove the Math.max bit of the second loop) but the log on line 69 is delayed because it is wrapped in a function which uses await to make sure that the loop has finished before (allowing for sync-like programming) diff --git a/javascript/async/async.js b/javascript/async/async.js new file mode 100644 index 0000000..6ae8a28 --- /dev/null +++ b/javascript/async/async.js @@ -0,0 +1,72 @@ +/** + * log a message with an iso datetime + * @param {*} msg msg to log + */ +const log = (msg) => { + console.log(msg, new Date().toISOString()); +}; + +/** + * sleep for a certain amount of ms + * @param {*} ms ms to sleep + */ +const sleep = (ms) => { + return new Promise((resolve) => setTimeout(resolve, ms)); +}; + +/** + * "asyncForEach" method on every array, allowing for async functionality on any array + */ +if (!Array.prototype.asyncForEach) { + Array.prototype.asyncForEach = async function (func) { + "use strict"; + + if (this == null) { + throw new TypeError( + "Array.prototype.asyncForEach called on null or undefined" + ); + } + + if (typeof func !== "function") { + throw new TypeError(); + } + + var t = Object(this); + + return await Promise.allSettled( + t.map(async (item) => { + return await func(item); + }) + ); + }; +} + +// array is not in order, this will be used to test +const items = [200, 1000, 400, 300]; + +// starting test +log("Starting async test"); + +// because of the sleep we expect the responses from small -> large +items.asyncForEach(async (item) => { + await sleep(item); + log(item); +}); + +// because the previous foreach is async we expect this to run immediately. +log("after async foreach (or before? because async..)"); + +async function testWithAwait() { + await items.asyncForEach(async (item) => { + // this will sleep for whatever the largest amount in items is + currentItem. + // this will make sure test items are displayed AFTER the first batch of items. + // this doesn't impact the result because the code IN the foreach will still run async (just with a delayed console.log) + await sleep(Math.max(...items) + item); + log(`Async with wait: ${item}`); + }); + + // because of the await on line 60 this will only run AFTER the async foreach has been handled + log("after testWithAwait"); +} + +testWithAwait(); diff --git a/javascript/async/image.png b/javascript/async/image.png new file mode 100644 index 0000000000000000000000000000000000000000..f1905bf5df0c0e4ad8f80aa67eba0fd33dadc785 GIT binary patch literal 38677 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 literal 0 HcmV?d00001 diff --git a/js-ts-frameworks/Angular/state/HttpWithStateSettingsService.ts b/js-ts-frameworks/Angular/state/HttpWithStateSettingsService.ts new file mode 100644 index 0000000..fec8661 --- /dev/null +++ b/js-ts-frameworks/Angular/state/HttpWithStateSettingsService.ts @@ -0,0 +1,71 @@ +import { Injectable } from "@angular/core"; +import { Store } from "@ngxs/store"; +import { first } from "rxjs/operators"; + +import { Observable, Subject } from "rxjs"; +import { HttpClient, HttpResponse } from "@angular/common/http"; +import * as resolvePath from "object-resolve-path"; + +@Injectable({ + providedIn: "root", +}) +export abstract class HttpWithStateSettingsService { + constructor( + public httpWithStateSettingsServiceStore: Store, + public httpWithStateSettingsServiceHttp: HttpClient, + public settingsKey: string + ) {} + + /** + * Higher order component which will resolve the current backend settings from the state + * @param callback + */ + public withSettings( + callback: (settings) => Observable + ): Observable { + return this.withState((state) => { + return resolvePath(state, this.settingsKey); + }, callback); + } + + /** + * get http response with settings + * @param getUrl function to construct the url. fe: (settings) => { const { root, principals } = settings; return `${root}/${principals}`;} + */ + public getResponse( + getUrl: (settings: TSettings) => string + ): Observable> { + return this.withSettings((settings: TSettings) => { + return this.httpWithStateSettingsServiceHttp.get(getUrl(settings), { + observe: "response", + }); + }).pipe(first()); + } + + /** + * Higher order component which will resolve the current backend settings from the state + * @param callback + */ + private withState( + selector: (state) => any, + callback: (settings) => Observable + ): Observable { + const subject = new Subject(); + this.httpWithStateSettingsServiceStore + .select(selector) + .pipe(first()) + .subscribe((data) => { + callback(data).subscribe( + (result) => { + subject.next(result); + subject.complete(); + }, + (error) => { + subject.error(error); + } + ); + }); + + return subject.asObservable(); + } +} diff --git a/js-ts-frameworks/React/tests/redux-promise-middleware.spec.js b/js-ts-frameworks/React/tests/redux-promise-middleware.spec.js new file mode 100644 index 0000000..b8e38eb --- /dev/null +++ b/js-ts-frameworks/React/tests/redux-promise-middleware.spec.js @@ -0,0 +1,66 @@ +import { PENDING, FULFILLED, REJECTED } from "redux-promise-middleware"; + +export default function handleReduxPromiseMiddleware( + reducer, + actionType, + initialState, + statusKey, + meta = {} +) { + it(`should handle ${PENDING} (redux-promise-middleware)`, () => { + let taintedState = Object.assign({}, initialState); + taintedState[statusKey] = "TAINTED"; + + const pendingAction = { + type: `${actionType}_${PENDING}`, + payload: PENDING, + meta: meta, + }; + + expect(reducer(taintedState, pendingAction)[statusKey]).toEqual(PENDING); + }); + + it(`should handle ${REJECTED} (redux-promise-middleware)`, () => { + let taintedState = Object.assign({}, initialState); + taintedState[statusKey] = "TAINTED"; + + const rejectAction = { + type: `${actionType}_${REJECTED}`, + payload: REJECTED, + meta: meta, + }; + + expect(reducer(taintedState, rejectAction)[statusKey]).toEqual(REJECTED); + }); + + it(`should handle ${FULFILLED} (redux-promise-middleware)`, () => { + let taintedState = Object.assign({}, initialState); + taintedState[statusKey] = "TAINTED"; + + const fulfillAction = { + type: `${actionType}_${FULFILLED}`, + payload: FULFILLED, + meta: meta, + }; + + expect(reducer(taintedState, fulfillAction)[statusKey]).toEqual(FULFILLED); + }); +} + +/** +USAGE: +import { + handleReduxPromiseMiddleware +} from "redux-promise-middleware.spec.js"; +handleReduxPromiseMiddleware( + reducer, + "ACTION_KEY", + initialState, + "status", { + data: { + access_token: "", + token_type: "" + } + } +); +**/ diff --git a/typescript/geo/radiusToPolygon/helpers.ts b/typescript/geo/radiusToPolygon/helpers.ts new file mode 100644 index 0000000..e9088f6 --- /dev/null +++ b/typescript/geo/radiusToPolygon/helpers.ts @@ -0,0 +1,52 @@ +import { Point } from "./point"; + +/** + * Converts an angle in radians to an angle in degrees by multiplying with 180 and dividing by 180 + * @param angleInRadians + */ +export const radiansToDegrees = (angleInRadians: number) => { + return (angleInRadians * 180) / Math.PI; +}; + +/** + * Convert an angle in degrees to an angle in radians by multiplying by PI and dividing the result by 180 + * https://www.mathwarehouse.com/trigonometry/radians/convert-degee-to-radians.php + * @param angleInDegrees + */ +export const degreesToRadians = (angleInDegrees: number) => { + return (angleInDegrees * Math.PI) / 180; +}; + +/** + * Convert a point object to an array of longitude,latitude + * @param point + */ +export const pointToLongLatArray = (point: Point) => { + return [point.longitude, point.latitude]; +}; + +/** + * Get a new point given a point, distance and bearing + * https://www.igismap.com/formula-to-find-bearing-or-heading-angle-between-two-points-latitude-longitude/#:~:text=Here%20is%20the%20formula%20to,%E2%80%93%20sin%20la1%20*%20sin%20la2) + */ +export const getRelativePointByDistance = ( + point: Point, + distance: number, + bearing: number +): Point => { + const latitude = Math.asin( + Math.sin(point.latitude) * Math.cos(distance) + + Math.cos(point.latitude) * Math.sin(distance) * Math.cos(bearing) + ); + const longitude = + point.longitude + + Math.atan2( + Math.sin(bearing) * Math.sin(distance) * Math.cos(point.latitude), + Math.cos(distance) - Math.sin(point.latitude) * Math.sin(latitude) + ); + + return { + latitude: radiansToDegrees(latitude), + longitude: radiansToDegrees(longitude), + }; +}; diff --git a/typescript/geo/radiusToPolygon/index.ts b/typescript/geo/radiusToPolygon/index.ts new file mode 100644 index 0000000..142401b --- /dev/null +++ b/typescript/geo/radiusToPolygon/index.ts @@ -0,0 +1,55 @@ +import { + degreesToRadians, + getRelativePointByDistance, + pointToLongLatArray, +} from "./helpers"; +import { Point } from "./point"; + +/** + * + */ +const options = { + /** + * IERS Equatorial Radius of the earth + * https://en.wikipedia.org/wiki/Earth_ellipsoid + */ + equatorialRadiusInMeters: 6378136.6, + + /** + * Number of edges to create in the polygon + * Use 360 to get a true circle + */ + numberOfEdges: 64, +}; + +const input = { + center: { latitude: 51.7543453, longitude: 5.5526647 } as Point, + radius: 2000, +}; + +const coordinates = []; +for (var i = 0; i < options.numberOfEdges; ++i) { + const newPoint = getRelativePointByDistance( + { + latitude: degreesToRadians(input.center.latitude), + longitude: degreesToRadians(input.center.longitude), + }, + input.radius / options.equatorialRadiusInMeters, + // find bearing https://www.igismap.com/formula-to-find-bearing-or-heading-angle-between-two-points-latitude-longitude/ + (2 * Math.PI * -i) / options.numberOfEdges + ); + coordinates.push(pointToLongLatArray(newPoint)); +} +// make sure the circle is closed by pushing a copy of the first element +coordinates.push(coordinates[0]); + +console.log( + JSON.stringify( + { + type: "Polygon", + coordinates: [coordinates], + }, + null, + 2 + ) +); diff --git a/typescript/geo/radiusToPolygon/point.ts b/typescript/geo/radiusToPolygon/point.ts new file mode 100644 index 0000000..e6b7eb2 --- /dev/null +++ b/typescript/geo/radiusToPolygon/point.ts @@ -0,0 +1,4 @@ +export interface Point { + latitude: number; + longitude: number; +} diff --git a/typescript/inheritance-and-interfaces.ts b/typescript/inheritance-and-interfaces.ts new file mode 100644 index 0000000..65eb09f --- /dev/null +++ b/typescript/inheritance-and-interfaces.ts @@ -0,0 +1,213 @@ +/** + * interface which specifies all properties of a living being + */ +interface IlivingBeing { + name: string; +} + +/** + * Interface which specifies the bark method + */ +interface IBark { + bark(): string; // functie die een string returned +} + +// You can't instantiate an abstract class so this class is merely used as a base +abstract class animal implements IlivingBeing { + name: string; + age: number; + + constructor(name: string, age: number) { + this.name = name; + this.age = age; + } + + makeNoise() { + return "No sound!"; + } +} + +/** + * The feline class extends the animal class, as such it gets all properties and methods of the animal class + */ +class feline extends animal { + // we receive a name and age and pass it to the super class (animal) + constructor(name: string, age: number) { + super(name, age); + } + + // override the makeNoise method for felines + makeNoise() { + return "Miaauw"; + } +} + +/** + * The canine class extends animal but aditionally implements the bark interface + * This means that we can put canine's in three groups now: + * - objects that implement IBark + * - Animals + * - Canines + * + * we can mark canine as abstract because we have subdivided the canines into + * dogs and wolves below. + */ +abstract class canine extends animal implements IBark { + constructor(name: string, age: number) { + super(name, age); + } + + makeNoise() { + return this.bark(); + } + + bark(): string { + return "Woof not implemented"; + } + + howl() { + return "Hooooowwwllll!"; + } +} + +/** + * The dog class simply extends canine and overrides the bark method + * Because dogs are happy creatures they can also wag their tails + */ +class dog extends canine { + constructor(name: string, age: number) { + super(name, age); + } + + wagTail() { + return "happy"; + } + + bark() { + return "wooof"; + } +} + +/** + * Simply extends from canine and implements a louder bark (more o's in wooof) + */ +class wolf extends canine { + constructor(name: string, age: number) { + super(name, age); + } + + bark() { + return "wooooof"; + } +} + +/** + * A rat is what we call those teeny tiny dogs (like chihuahuas) + */ +class rat extends canine { + size: string; + + constructor(name: string, age: number) { + super(name, age); + } + + howl() { + return "garble " + super.howl(); + } + + bark() { + return "woof"; + } +} + +// +// The classes below show that we can even group "animals" and "others" together +// if they all implement the same (ILivingBeing) interface +// + +class person implements IlivingBeing { + name: string; + + constructor(name: string) { + this.name = name; + } +} + +/** + * Aliens don't really have a name (they have a designation, military race and all) + * in order to class them as IlivingBeing we need to give them a name (because humans like to name everything) + * we do this in the constructor by simply using the aliens designation as its name. + */ +class alien implements IlivingBeing { + name: string; + designation: number; + + constructor(num: number) { + this.designation = num; + this.name = this.designation.toString(); + } +} + +// _______ _ +// |__ __| | | +// | | ___ ___| |_ _ __ _ _ _ __ ___ +// | |/ _ \/ __| __| | '__| | | | '_ \/ __| +// | | __/\__ \ |_ | | | |_| | | | \__ \ +// |_|\___||___/\__| |_| \__,_|_| |_|___/ + +// object declarations +let cat1 = new feline("cat1", 6); +let cat2 = new feline("cat2", 4); +let dog1 = new dog("dog1", 12); +let wolf1 = new wolf("wolf", 7); +let dog2 = new dog("dog2", 4); +let rat1 = new rat("rattekop", 6); +let person1 = new person("John"); +let alien1 = new alien(12351); + +// Arr declarations +let animals: animal[] = [cat1, cat2, dog1, wolf1, dog2, rat1]; +let dogs: dog[] = [dog1, dog2]; +let canines: canine[] = [dog1, dog2, wolf1, rat1]; +let barkers: IBark[] = [...canines]; +let felines: feline[] = [cat1]; +let livingBeings: IlivingBeing[] = [ + cat1, + cat2, + dog1, + wolf1, + dog2, + rat1, + person1, + alien1, +]; + +// for loops +console.log("Dog Wagtails"); +dogs.forEach((dog) => { + console.log(dog.wagTail()); +}); +console.log(" "); + +console.log("Canine Howls"); +canines.forEach((canine) => { + console.log(canine.howl()); +}); +console.log(" "); + +console.log("Animals makeNoise"); +animals.forEach((animal) => { + console.log(animal.makeNoise()); +}); +console.log(" "); + +console.log("Beings name"); +livingBeings.forEach((livingbeing) => { + console.log(livingbeing.name, livingbeing); +}); +console.log(" "); + +console.log("Barkers"); +barkers.forEach((barker) => { + console.log(barker.bark()); +});