populate object properties using lambda expression in typescript - node.js

Newbie Alert! I feel silly asking this question but I need someone to teach me the correct syntax.
I have code that looks like this:
let thing: INewThing;
thing.personId = another.personId;
thing.address = another.work.address;
thing.greades = another.subjectInfo.grades;
thing.isCurrent = another.student.isCurrent;
I know it can be written cleaner. I want to use a lamda expression, something like this:
let thing: INewThing => {
personId = another.personId,
address = another.work.address,
grades = another.subjectInfo.grades,
isCurrent = another.student.isCurrent
} as IThingUpdate;
I have looked and looked for an example. I have yet to find one that works for me. It's just syntax but no matter what I try it doesn't work.

You're just looking to create a new object, which is a pretty different thing from a "lambda" (function). Just declare the object. You don't need a function.
const thing = {
personId: another.personId,
address: another.work.address,
// use the correct spelling below - no 'greades'
grades: another.subjectInfo.grades,
isCurrent: another.student.isCurrent,
};
If the another is typed properly, that should be sufficient.
If the another object had more properties using the same path, another option would be to destructure those properties out, then declare the object with shorthand, eg:
const originalObj = { prop: 'val', nested: { foo: 'foo', bar: 'bar', unwanted: 'unwanted' } };
const { foo, bar } = originalObj.nested;
const thing = { foo, bar };
Destructuring like this, without putting the values into differently-named properties, helps reduce typos - if a property starts out, for example, as someLongPropertyName, putting it into a standalone identifier someLongPropertyName and then constructing an object with shorthand syntax ensures that the new object also has the exact property name someLongPropertyName (and not, for example, someLongPRopertyName - which isn't that uncommon of a mistake when using the more traditional object declaration format).
But since all the paths in your another object are different, this approach wouldn't work well in this particular situation.

Related

A strange mismatch not noticed by Typescript. How is this possible?

Apparently, Typescript doesn't seem to recognize the difference between an object like {} and a generic array [] by accepting last one as input of a function that requires an object with {}'s structure.
To resume my problem, this is a simplified example to replicate it:
type test = { [key: string]: any };
let x: test = ["x", "y", "z"];
Actually, Typescript seems to accept this. How is this possible?
Note: The situation I ran into is more similar to this:
type fooType = { [key: string]: any };
const fooFunction<T extends fooType>(input: T) => // code...
fooFunction([]); // No red underline
But you can consider the first example. It's the same.
The main idea is to create a function that accepts only objects with a key (type string) and a value of any type.
Thank you in advance for the answers!
Differentiating between plain objects and other things (like arrays, or even functions) can be frustrating in JavaScript (and therefore Typescript).
Since an array is an object, you need a type that excludes arrays. For completeness, you may also want to exclude other non-plain objects, like functions, dates, regexes, etc, but I'll just focus on arrays.
Using your example, here are some approaches:
1. Exclude objects with numeric indexes
function fooFunction<T extends {
[key: string]: any,
[index: number]: never
}>(input: T) { }
fooFunction(['']); // Will have red underline!
fooFunction([]); // This will NOT have an underline!
In the above case, we're saying that T cannot have any numeric indexes. There is an edge case, though: an empty array has type never[], which also has no numeric indexes!
2. Exclude array-specific fields
Another approach is to identify some property common to arrays that won't be in any of the objects you plan to pass through your function:
function fooFunction<T extends {
map?: never,
}>(input: T) { }
fooFunction(['']); // Will have red underline!
fooFunction([]); // So will this!
3. Narrow the parameter type
The cleanest approach is to narrow your generic at the parameter to exclude arrays. The following example uses a utility type that returns never for lots of non-plain-object inputs (but not all of them):
type FancyObject = any[]|Function|Date|RegExp|Error
type PlainObject<T> = T extends FancyObject
? never
: T extends { [key: string]: any }
? T
: never;
function fooFunction<T>(input: PlainObject<T>) {}
fooFunction(['']); // Will have red underline!
fooFunction([]); // So will this!
fooFunction({ hello: 'world' }) // This is fine!

TypeScript shared configuration object with typehinting

I am developing an API library and I am curious about how should endpoint configuration problem should be approached in Node.js with TypeScript. I want all endpoint configuration to be contained within one entity.
I currently have this approach in place:
ApiConstants.ts
------------------------
const BASE_DOMAIN = 'https://api.example.com';
export default Object.freeze({
BASE_DOMAIN: {
V1: `${BASE_DOMAIN}/v0.1`,
V2: `${BASE_DOMAIN}/v0.2`,
V3: `${BASE_DOMAIN}/v0.3`,
},
PATH: {
CATS: '/animals/cats',
},
});
It does the job, I can use it in any class by importing it and accessing the values. The problem is that I want to restrict functions to only accept values declared within this object. When request constructing function should display invalid type intellisense when value is passed which is not a part of this object.
Desired type would look something like this. Path must be declared within ApiConstants.PATH object.
function makeRequest(path: ApiConstants.PATH) {
...
}
How can such behavior be achieved?
function makeRequest(path: keyof typeof ApiConstants.PATH) {
// ...
}
See in Playground
In general, you probably want to do something like
const ApiConstants = Object.freeze({
BASE_DOMAIN: {
V1: `${BASE_DOMAIN}/v0.1`,
V2: `${BASE_DOMAIN}/v0.2`,
V3: `${BASE_DOMAIN}/v0.3`,
},
PATH: {
CATS: '/animals/cats',
},
});
type ApiConstants = typeof ApiConstants;
export default ApiConstants;
to avoid having to use typeof ApiConstants all the time. Note that this means ApiConstants is both a value and a type—Typescript is OK with this, and will know from context what you’re doing, but some programmers find it confusing. A common naming convention is to use an initial lowercase letter for the value, and an initial uppercase for the type, as in const apiConstants = /*…*/; and type ApiConstants = typeof apiConstants;.
On the other hand, it’s kind of convenient that your default export is both the value and the type.
You might also want to add
export type ApiPaths = keyof ApiConstants['PATH'];
We use ['PATH'] because we’re using the type ApiConstants here, not the value. We could still use the value but we’d have to add typeof to it again, as in keyof typeof ApiConstants.PATH.

How to add TypeScript Interface to shorthand variable?

How to set TypeScript interface to variable looking like this:
const { body, statusCode } = await got.post('smth', requestOpts)
How can I set the interface to ex. body. I would like to assign there an interface with potential requests from the server. An interface that I'm willing to implement looks like that:
interface ResponseBody {
data: [{ username: string }]
}
I tried different things to attach this interface to the upper variable body but I'm getting build errors. The only workaround that I have here is to do not use shorthand variable but that's not my goal - I wonder if there are solutions for such issue.
TypeScript Playground
You'd need to annotate the whole object, like this:
const { body, statusCode }: { body: ResponseBody, statusCode: number } =
await got.post<ResponseBody>('users', requestOpts)
While it would be nice if you could annotate the individual destructured variables inside the object, this is not currently possible. The syntax const { body: ResponseBody } = ... can't work because JavaScript interprets this as assigning to a new variable name (so there'd be a variable named ResponseBody holding the value that was in the body property). Perhaps something like const { body::ResponseBody, statusCode::number } would work instead, as requested in microsoft/TypeScript#29526. But for now this is not possible, and you have to annotate the whole object.
That's the answer to the question as asked.
Do note that for your particular example, though, the got.post() method has a call signature that's generic in the type you expect the body property to be. So you can write
const { body, statusCode } = await got.post<ResponseBody>('users', requestOpts);
to get a strongly typed body variable with a minimum of extra keystrokes.
Playground link to code

Enum attribute in lit/lit-element

We are trying to build a component with a property variant that should only be set to "primary" or "secondary" (enum). Currently, we are just declaring the attribute as a String, but we were wondering if there is a better way for handling enums? For example, should we validate somehow that the current value is part of the enum? Should we throw an error if not?
I asked this question on Slack and the answers I got lean towards declaring the property as String and use hasChanged() to display a warning in the console if the property value is invalid.
Standard HTML elements accept any string as attribute values and don't throw exceptions, so web components should probably behave the same way.
This all sounds reasonable to me.
If you're using TypeScript I'd recommend just using strings. You can use export type MyEnum = 'primary' | 'secondary' to declare it and then use #property() fooBar: MyEnum to get build time checking. You can use #ts-check to do this in plain JS with #type MyEnum too.
This works well if the enums are for component options or that map to server-side enums that will get validated again.
However, if you want to validate user input into enums or loop through them a lot this is less good. As the JS runs it has no visibility of the type. You need an object dictionary, something like:
const MyEnum = Object.freeze({
primary: 'primary',
secondary: 'secondary'
});
// Enforce type in TS
const value: keyof MyEnum;
// Validate
const validated = MyEnum[input.toLower()];
// Loop
for(const enumVal of Object.keys(MyEnum)) ...
// Or Convert to a different value type
const MyEnum = Object.freeze({
primary: 1,
secondary: 2
});
These are somewhat idiosyncratic. Again, if you're using TypeScript it has an enum keyword that compiles to something like this and I'd use that rather than rolling your own. Strings are the better option unless you need to validate, loop or convert the values.

Run getter in javascript object defined by Object.setProperty()?

If I create an object property via Object.defineProperty() with a getter/setter method (an accessor descriptor) like:
var myObj = function myObj() {
var myFoo = 'bar';
Object.defineProperty(this, 'foo', {
enumerable: true,
get: function() {
return myFoo;
},
set: function(newValue) {
myFoo = newValue;
}
});
return this;
};
If I do something like var f = new myObj(); console.log(f) in Node, the output is something like:
{ foo: [Getter/Setter] }
console.log(f.foo) gets the proper 'bar' value, but is there a way to indicate that upon logging/inspecting, it should just run the getter and show the value?
First, it's important to understand why this happens. The logging functions don't run getters by design because your getter function could have side effects, whereas the logging function can guarantee that getting the value of a primitive doesn't.
When you pass an object to console.log, it's really just passing it off to the util module's inspect to format into human-readable text. If we look there, we see that the very first thing it does is check the property descriptor, and if the property has a getter, it doesn't run it. We can also see that this behavior is unconditional – there's no option to turn it off.
So to force getters to run, you have two options.
The simplest is to convert your object to a string before handing it off to console.log. Just call JSON.stringify(obj, null, 4), which will produce reasonably human-readable output (but not nearly as nice as util.inspect's). However, you have to take care to ensure that your object doesn't have any circular references and doesn't do something undesired in a toJSON function.
The other option is to implement a inspect function on your object. If util.inspect sees a function called inspect on an object, it will run that function and use its output. Since the output is entirely up to you, it's a much more involved to produce output that looks like what you'd normally get.
I'd probably start by borrowing code from util and stripping out the part about checking for getters.
This behavior is certainly intentional. I know I wouldn't want all the getter functions on an object running whenever I logged that object; that sounds like potential a debugging landmine, where debugging could alter the state of my program.
However, if indeed that is the behavior you want, you can add a new function:
Object.getPrototypeOf(console).logWithGetters = function(obj) {
var output = {};
var propNames = Object.getOwnPropertyNames(obj);
for(var i=0; i<propNames.length; ++i) {
var name = propNames[i];
var prop = Object.getOwnPropertyDescriptor(obj, name);
if(prop.get) {
output[name] = prop.get();
} else {
output[name] = obj[name];
}
}
// set output proto to input proto; does not work in some IE
// this is not necessary, but may sometimes be helpful
output.__proto__ = obj.__proto__;
return output;
}
This allows you to do console.logWithGetters(f) and get the output you want. It searches through an object's properties for getters (checking for the existence of Object.getOwnPropertyDescriptor(obj, propName).get) and runs them. The output for each property is stored in a new object, which is logged.
Note that this is a bit of a hacky implementation, as it doesn't climb the object's prototype chain.

Resources