
Question:
I want to define a global function that is available everywhere, without the need to import the module when used.
This function aims to replace the safe navigation operator (?) available in C#. For the sake of readability, I don't want to prefix the function with a module name.
Global.d.ts:
declare function s<T>(someObject: T | null | undefined, defaultValue?: T | null | undefined) : T;
Global.tsx:
///<reference path="Global.d.ts" />
export function s<T>(object: T | null | undefined, defaultValue: T | null = null = {} as T) : T {
if (typeof object === 'undefined' || object === null)
return defaultValue as T;
else
return object;
}
App.tsx (root TypeScript file):
import 'Global';
Other TSX file (method usage):
s(s(nullableVar).member).member; //Runtime error
This compiles fine, however, in the browser this throws 's is not a function
'.
You're defining the type for the compiler, but not actually attaching it to the global namespace — window
in the browser, global
in node. Instead of exporting it from the module, attach it. For isomorphic use, use something like...
function s() { ... }
// must cast as any to set property on window
const _global = (window /* browser */ || global /* node */) as any
_global.s = s
You can also ditch the .d.ts
file and declare the type in the same file using declare global
, e.g.
// we must force tsc to interpret this file as a module, resolves
// "Augmentations for the global scope can only be directly nested in external modules or ambient module declarations."
// error
export {}
declare global {
function s<T>(someObject: T | null | undefined, defaultValue?: T | null | undefined) : T;
}
const _global = (window /* browser */ || global /* node */) as any
_global.s = function<T>(object: T | null | undefined, defaultValue: T | null = null) : T {
if (typeof object === 'undefined' || object === null)
return defaultValue as T;
else
return object;
}
Answer2:global.ts(x)
needs just a little tweak to be a valid "global module" <em>(a module with side effects only)</em>: remove the export
keyword and add some code to augment the global object. You can also provide the global declaration in the same file and remove global.d.ts
.
function _s<T>(object: T | null, defaultValue: T = {} as T) : T {
return object == null
? defaultValue
: object as T;
}
// Global declaration
declare var s: typeof _s;
// Global scope augmentation
var window = window || null;
const _global = (window || global) as any;
_global.s = _s;
To use it, just import the module once, for instance in App.tsx
via a global import: import './global';
.
Tested with mocha, chai, ts-node:
<pre class="lang-js prettyprint-override">import { expect } from 'chai';
import './global'; // To do once at app bootstrapping
describe('global s()', () => {
it('should replace null with empty object', () => {
const result = s(null);
expect(result).to.eql({});
});
it('should replace undefined with empty object', () => {
const result = s(undefined);
expect(result).to.eql({});
});
it('should replace null with the given default value', () => {
const defVal = { def: 'val' };
const result = s(null, defVal);
expect(result).to.eql({ def: 'val' });
});
it('should preserve defined object', () => {
const object = { bar: 'a' };
const result = s(object);
expect(result).to.eql(object);
});
});