Skip to content

Instantly share code, notes, and snippets.

@fearthecowboy
Created May 2, 2018 21:09
Show Gist options
  • Save fearthecowboy/5ce5376276cd18bdd9065e56fd7ad47a to your computer and use it in GitHub Desktop.
Save fearthecowboy/5ce5376276cd18bdd9065e56fd7ad47a to your computer and use it in GitHub Desktop.
Typescript conditional types oddity
export interface Dictionary<T> {
[key: string]: T;
}
export interface Pair<KEY, VALUETYPE> {
key: KEY;
value: VALUETYPE;
}
function* itemsa<V>(array: Array<V>): Iterable<Pair<number, V>> {
for (let key = 0; key < array.length; key++) {
yield { key, value: array[key] };
}
}
function* itemsd<V>(dictionary: Dictionary<V>): Iterable<Pair<string, V>> {
if (dictionary) {
for (const key of Object.getOwnPropertyNames(dictionary)) {
yield { key, value: dictionary[key] };
}
}
}
export function* values<T>(dictionary?: Dictionary<T> | Array<T>): Iterable<T> {
if (Array.isArray(dictionary)) {
for (let key = 0; key < dictionary.length; key++) {
yield dictionary[key];
}
return;
}
if (dictionary) {
for (const key of Object.getOwnPropertyNames(dictionary)) {
yield dictionary[key];
}
}
}
function* akeys<V>(array: Array<V>): Iterable<number> {
for (let key = 0; key < array.length; key++) {
yield key;
}
}
function* dkeys<V>(dictionary?: Dictionary<V>): Iterable<string> {
if (dictionary) {
for (const key of Object.getOwnPropertyNames(dictionary)) {
yield key;
}
}
}
export function keys<V, T extends (Dictionary<V> | Array<V>)>(dictionary?: T): T extends Dictionary<V> ? Iterable<string> : Iterable<number> {
return Array.isArray(dictionary) ?
<T extends Dictionary<V> ? Iterable<string> : Iterable<number>>akeys(dictionary) :
<T extends Dictionary<V> ? Iterable<string> : Iterable<number>>dkeys((<Dictionary<V>>dictionary));
}
export function length<T>(dictionary?: Dictionary<T> | Array<T>): number {
if (Array.isArray(dictionary)) {
return dictionary.length;
}
return dictionary ? Object.getOwnPropertyNames(dictionary).keys.length : 0;
}
// usage:
const dict = {
age: 100,
name: "garrett"
}
const arr = ["every", "good", "boy", "deserves", "fudge"];
for( const each of keys(dict)) {
console.log(each);
}
for( const each of keys(arr)) {
console.log(each);
}
for (const each of values(dict)) {
console.log(each);
}
for (const each of values(arr)) {
console.log(each);
}
for (const each of items(dict)) {
// type of each is Pair<string, string|number>
console.log(each);
}
for (const each of items(arr)) {
// type of each is Pair<number, string>
console.log(each);
}
for (const each of items_bad(dict)) {
// type of each is Pair<string, {}>
console.log(each);
}
for (const each of items_bad(arr)) {
// type of each is Pair<number, {}>
console.log(each);
}
export function items<V, T extends (Array<V> | Dictionary<V>)>(input?: T & (Array<V> | Dictionary<V>)): T extends Dictionary<V> ? Iterable<Pair<string, V>> : Iterable<Pair<number, V>> {
return Array.isArray(input) ?
<T extends Dictionary<V> ? Iterable<Pair<string, V>> : Iterable<Pair<number, V>>>itemsa(input) :
<T extends Dictionary<V> ? Iterable<Pair<string, V>> : Iterable<Pair<number, V>>>itemsd(<Dictionary<V>>input);
}
// when I comment out the '& (Array<V> | Dictionary<V>)' form the parameter
// the return type goes to Pair<keytype,{}> -- WHY?
export function items_bad<V, T extends (Array<V> | Dictionary<V>)>(input?: T /* & (Array<V> | Dictionary<V>) */ ): T extends Dictionary<V> ? Iterable<Pair<string, V>> : Iterable<Pair<number, V>> {
return Array.isArray(input) ?
<T extends Dictionary<V> ? Iterable<Pair<string, V>> : Iterable<Pair<number, V>>>itemsa(input) :
<T extends Dictionary<V> ? Iterable<Pair<string, V>> : Iterable<Pair<number, V>>>itemsd(<Dictionary<V>>input);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment