Declare type of a object key - javascript

I have a XInterface like so:
export interface XInterface {
foo: (() => Foo[]) | Foo[],
bar: string,
baz: number
}
Then, using the interface to declare an object I would like to the type of foo to be Foo[], like
const myObj: XInterface = {
[myFoo1, myFoo2],
'bar',
1
}
but as I am already using the : to declare my array of Foo, I don't know how to ensure that foo is an array, not a function that returns an array.
How can I achieve that?

This is a pattern I would follow. In the future you will pass this object to somewhere XInterface is expected, which does not know if foo is a function or an array. With that in mind, you will always have to check the content of foo. A better approach is to simply convert foo to a function.

You can define XInterface like:
foo: Foo[],
bar: string,
baz: int
and let suppose that you have a function
someFuntion(): Foo[] { .....};
so when you define your object
const myObj: XInterface = {
foo: someFuntion(),
bar: 'bar',
baz: 1
}

Related

How to set a functions parameter type to be a property KEY of ONE of a type's properties? Is this even possible?

How does one set a function's parameter type to accept a key of one of a type's properties? Is this even possible?
type A = {
['prop1']: string;
['props2']: boolean;
}
// arg would be either 'prop1' or 'prop2'
const example = (arg: property in A ) => {}
My assumption is that this is not even possible. If so, is there a way to get around this?
Is there a way to declare a type similar to how you can declare an object with an enum? Obviously, this is an object below, but could you do something similar with a type?
Example:
enum Enum {
bold = 'bold',
italic = 'italic',
}
const objWithEnumProperties: {[key in Enum]: any} = {
[Enum.bold]: '',
[Enum.italic]: ''
}
How to set a functions parameter type to be a property KEY of ONE of a types properties? Is this even possible?
Yes, using TypeScript keyof:
type Foo = { bar: string; baz: boolean; }
// arg is 'bar' | 'baz'
const fn = (arg: keyof Foo) => {}
Is there a way to declare a type similar to how you can declare a object with an enum?
You are probably wanting a const object assertion or an interface:
// Usable at runtime
const Foo = {
bar: 'bar',
baz: 'baz'
} as const;
// Only for typing
interface Foo {
bar: string;
baz: boolean;
}

TypeScript nested object interface with known subset of keys

I have a JavaScript object created like this.
const meta = {
baz: null
}
const response = {
foo: 1,
bar: {
foo: 2,
buzz: {
foo: 3,
...meta
},
...meta
},
...meta,
qux: ‘qux’
}
The response object is a plain object that can contain any key. We know the type of keys foo, baz, and qux. We don’t know the other key names but when they do exist, they are objects with a known interface. In fact, the response object is an instance of this interface.
How do I create a TypeScript interface expressing this logic? I want the compiler to allow defining objects like this:
const foo: MyInterface = {
foo: 1,
dynamicKey: {
baz: null
}
}
Index signatures might work if you are okay with this limitation.
This worked for my use case. The trick was to expand the index signature interface with all values. This has a limitation as was pointed out, but it's a much better situation than not having any type checking or autocompletion. My interface now looks like this.
interface MyInterface extends KnownInterface {
[key: string]: MyInterface | KnownInterface[keyof KnownInterface];
}

TypeScript - Define a subset of type

Say I have a type like so:
interface IAll {
foo: boolean,
bar: Function,
baz: number
}
instead of manually defining all the possible subtypes of IAll, like so:
interface IAll1 {
foo: boolean,
bar: Function,
}
interface IAll2 {
bar: Function,
baz: number
}
interface IAll3 {
foo: boolean,
}
interface IAll4 {
foo: boolean,
}
...etc
and then doing
type IAll = IAll1 | IAll2 | IAll3 ... etc.
Is there a way for TypeScript to statically check whether an object is a subtype or subset of another?
This is useful for some cases where we combine several subtypes or subsets to form a full type.
You can use Partial<T>. This will make all the properties in IAll optional:
type SubsetOfIAll = Partial<IAll>;

flow types with constant strings, and dependent types

Say I have the following constant string:
export default const FOO = 'FOO'
Say I import this in a flow annotated file like so:
import FOO from '../consts/Foo'
I then have a function:
const example = (foo : string) : {| type: FOO, foo: string |} => {
return {type: FOO, foo: foo}
}
This doesn't typecheck with:
6: const example = (foo : string) : {| type: FOO, foo: string |}=> {
^^^^^^^^^^^^^^ string. Ineligible value used in/as type annotation (did you forget 'typeof'?)
6: const example = (foo : string) : {| type: FOO, foo: string |}=> {
^^^^^^^^^^^^^^ FOO
So my questions are:
1) is it possible to use constants in flow types, how can I reproduce this behaviour?
2) Is it possible to do dependent types in flow? so for example, could I encode, through types, that the string that is returned must be the same string that is passed into the example function?
EDIT: Clarification to part 2: Is it possible to in some way indicate that the foo parameter passed into the example function is in fact the same string as the string at the foo key in the return object? Or to assert that the input and output have the same length (for say a shift cipher function). Or say contain a permutation of the same characters? (for a shuffle).
https://en.wikipedia.org/wiki/Dependent_type
Instead of declaring FOO as a const, declare it as a disjoint union with just one branch:
type FOO = "FOO"
Then your code can be updated like this:
const example = (foo : string) : {| type: FOO, foo: string |} => {
return {type: "FOO", foo: foo}
}
If you use any value besides the exact string literal "FOO" where a FOO is required, then it is a compile error.
If you would prefer to keep your constant, then you'll need to name the type differently, as they would collide. So you could do:
const FOO = "FOO"
type FooType = "FOO";
const example = (foo : string) : {| type: FooType, foo: string |} => {
return {type: FOO, foo: foo}
}
Unfortunately, I can't see a way to avoid duplicating the string literal, because type disjoint union definition syntax only permits literals and types, not variables even if they are constants.
Found a workaround for the issue,
Instead of using flow type inference we can specify the literal type
export default const FOO:'FOO' = 'FOO'
then in the function you can use as
const example = (foo : string) : {| type: typeof FOO, foo: string |} => {
return {type: FOO, foo: foo}
}
Because when you declare the constant type is inferred to be string also I believe flow doesn't support setting type definitions from variables or constants.

Node.js adding multiple properties to an object

I'm wondering if there's a better way of doing this:
module.exports.foo = "bar";
module.exports.bar = "baz";
module.exports.foobar = "foobaz";
Now, I've heard about 'with' (and its dangers) but it doesn't work here because it can't add new properties to an object, it can only change existing properties. In fact if you try to add new properties you end up clobbering the enclosing scope which isn't very nice.
Something like this would be nice:
module.exports += {
foo: "bar",
bar: "baz",
foobar: "foobaz"
}
But that converts both sides to strings before concatenating them which is not what I want.
Does such syntactic sugar exist in Node.JS-flavoured JavaScript? Maybe a function on 'module' to add lots of properties at once?
You could write a simple function to loop over them and add them to module.exports or you can try node.extend which tries to mimic jquery's $.extend()
Use Object.assign()
Object.assign(module.exports, {
foo: "bar",
bar: "baz",
foobar: "foobaz"
});
Consider using _.extend
So in your example:
module.exports += {
foo: "bar",
bar: "baz",
foobar: "foobaz"
}
Would be:
var _ = require('underscore');
_.extend(module.exports,
{
foo: "bar",
bar: "baz",
foobar: "foobaz"
}
);
Or, if you do not want any dependencies, using raw javascript:
function myExtend(dest, obj) {
for (key in obj) {
var val = obj[key];
dest[key] = val;
}
}
myExtend(module.exports,
{
foo: "bar",
bar: "baz",
foobar: "foobaz"
}
);
If you are using the latter method, make sure to check for edge cases, which I haven't here!

Categories

Resources