Skip to content

Swift-like extensions for JavaScript and TypeScript

License

Notifications You must be signed in to change notification settings

kyle-n/TypeSwift

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

TypeSwift

Swift offers a number of convenience variables and functions across its core types. When I went back to TypeScript after a summer of Swift projects, I missed a lot of these conveniences. So I've added them to TypeScript and JavaScript.

Installation

npm install typeswift

yarn add typeswift

Usage

For Node and single-page applications, import TypeSwift at the start of your index file.

// index.js
const TypeSwift = require('typeswift');

// rest of program...
// index.ts
import 'typeswift';

// rest of program...

TypeSwift also comes bundled as minified and non-minified scripts. Add one of these in HTML to use them in subsequent <script> tags.

<body>
...
</body>
<script src="/path/to/TypeSwift/_bundles/typeswift.min.js"></script>
<script>
var numbers = ['one', 'two', 'three'];
console.log(numbers.first) // 'one'
console.log(numbers.endIndex) // 2
</script>

Once TypeSwift is loaded, you can begin using its extensions to JS's base classes:

const nums = ['one', 'two', 'three'];
console.log(nums.first);          // 'one'
console.log(nums[0]);             // 'one'
console.log(nums.last)            // 'three'
console.log(nums.randomElement()) // 'two'

const objectsMayHaveId = [{id: 1}, {}, {id: 3}];
const ids = objectsMayHaveId.compactMap(object => object.id);
console.log(ids);                 // [1, 3]

const objectA = {one: 1, two: 2};
const objectB = {two: 'two', three: 'three'};
const merged = objectA.merging(objectB, (objA, objB) => objB);
console.log(merged);              // {one: 1, two: 'two', three: 'three'}

const letters = 'abc';
console.log(letters.reversed())   // 'cba'
console.log(letters.shuffled())   // 'cab'
console.log(letters.endIndex)     // 2

Features

TypeSwift includes a number of convenience methods based on their Swift counterparts. It does not include:

  • Functionality around managing memory
  • Functions that mutate primitive types, since JS primitives are immutable (e.x. you can't .toggle() booleans)
  • Functions with an extremely close equivalent in JavaScript (e.x. first(where: ) is just find())

API

type UniquingKeysCallback = (valueOne: any, valueTwo: any) => any;
type KeyedObject = {[key: string]: any};
type KeyValueAssertionCallback = <E>(key: string | number, value: E) => boolean;

declare global {
  interface Object {
    readonly isEmpty: boolean;
    readonly count: number;
    readonly first: any;
    readonly randomElement: () => any;
    readonly merge: (objectToMerge: KeyedObject, uniquingKeysWith: UniquingKeysCallback) => void;
    readonly merging: (objectToMerge: KeyedObject, uniquingKeysWith: UniquingKeysCallback) => KeyedObject;
    readonly removeValue: (forKey: string | number) => void;
    readonly removeAll: () => void;
    readonly contains: (where: KeyValueAssertionCallback) => boolean;
    readonly allSatisfy: (callback: KeyValueAssertionCallback) => boolean;
  }
}
declare global {
  interface Array<T> {
    readonly first: T | undefined;
    readonly last: T | undefined;
    readonly isEmpty: boolean;
    readonly randomElement: () => T | undefined;
    readonly insert: (element: T, at: number) => void;
    readonly remove: (at: number) => T | undefined;
    readonly compactMap: (callback: (element: T, index?: number, parent?: Array<T>) => any | undefined) => Array<any>;
    readonly shuffle: () => void;
    readonly shuffled: () => Array<T>;
    readonly swapAt: (indexA: number, indexB: number) => void;
    readonly startIndex: number | undefined;
    readonly endIndex: number | undefined;
  }
}
declare global {
  interface String {
    readonly isEmpty: boolean;
    readonly inserted: (substring: string, at: number) => string;
    readonly first: string | undefined;
    readonly last: string | undefined;
    readonly randomElement: () => string | undefined;
    readonly map: (callback: (char: string, index?: number, parent?: string) => string) => string;
    readonly compactMap: (callback: (char: string, index?: number, parent?: string) => string | undefined) => string;
    readonly forEach: (callback: (char: string, index?: number, parent?: string) => void) => void;
    readonly reduce: <R>(callback: (result: R, char: string, index?: number) => R, initialValue: R) => R;
    readonly sorted: () => string;
    readonly reversed: () => string;
    readonly shuffled: () => string;
    readonly startIndex: number | undefined;
    readonly endIndex: number | undefined;
    readonly prefix: (callback: (char: string, index?: number, parent?: string) => boolean) => string;
  }
}
declare global {
  interface Number {
    readonly quotientAndRemainderOf: (dividingBy: number) => [number, number];
    readonly isMultipleOf: (number: number) => boolean;
    readonly isZero: boolean;
  }

  // static properties (e.x. Number.zero)
  interface NumberConstructor {
    readonly zero: 0;
  }
}
declare global {
  // static properties (e.x. Boolean.random())
  interface BooleanConstructor {
    readonly random: () => boolean;
  }
}

Q&A

Can I still use arr[0]? Do I have to use arr.first?

TypeSwift doesn't break any existing JS functionality, it just extends it. All existing JS methods and variables will still work.

Why should I install a whole package just to write arr.last?

If you don't think TypeSwift's extensions would help you, that's cool. I prefer writing arr.last instead of arr[arr.length - 1], but it's all personal preference.

I would like X feature from Swift added.

Help with features would be greatly appreciated. Please check out existing code to see the style and open a PR.