Using generic type with Record in Typescript - typescript-typings

I'm having a hard time understanding why this doesn't work. I'using TS 3.8.3
type MyType<T extends string> = Record<T, string | null>
const myFunction = <T extends string>() => {
const myObject: MyType<T> = {};
console.log(myObject);
};
I'm getting this error: Type '{}' is not assignable to type 'Record '.
T extends string and Record needs a generic type extending any from my understanding of Typescript this should work.
Could anyone help me figure out what I am missing?
Thank you!

Related

Typescript Conditional Statement doesn't filter values?

When I use Typescript Conditional Statement it doesn't remove the values. For example, if the type of "ErrorsType" is string | null, and I use ErrorsType extends string ? InHereTheValueOf'ErrorsType'IsStill'String'|'null'InsteadOf'String' : InHereTheValueOf'ErrorsType'IsStill'String'|'null'InsteadOf'null'
Here is my code:
export const BotErrors = {
test: (reason: string) => `This error bc of ${reason}`,
ValueTooLow: 'You can not reduce value below 0',
};
type ErrorsType = typeof BotErrors;
export default class SomeError<T extends keyof ErrorsType> extends Error {
constructor(code: T, ...args: ErrorsType[T] extends Function ? Parameters<ErrorsType[T]> : []) {
let msg: string | Function = BotErrors[code];
if (typeof msg === 'function') msg = msg(...args) as string;
super(msg);
}
}
At ErrorsType[T] got error: Type 'Function & { test: (reason: string) => string; ValueTooLow: string; }[T]' does not satisfy the constraint '(...args: any) => any'. Because of the BotErrors have both function and string as it's value.
After I add // #ts-nocheck everything is fine, even with type definition. But I'm try wondering if there is any way to not ignore errors?
The problem with
ErrorsType[T] extends Function ? Parameters<ErrorsType[T]> : []
is that the Parameters<T> utility type is defined as
type Parameters<T extends (...args: any) => any> =
T extends (...args: infer P) => any ? P : never;
where its type parameter is constrained to the function type expression (...args: any) => any,
while you have only checked the ErrorsType[T] type argument against the Function interface. And while (...args: any) => any is assignable to Function, the reverse is not true: just because something is a Function the compiler does not know that it is a (...args: any) => any. The Function type is best avoided in general, since it represents untyped function calls.
Anyway it's easy enough to fix this; just change the conditional type check from Function to (...args: any) => any:
ErrorsType[T] extends (...args: any) => any ? Parameters<ErrorsType[T]> : []
Or the equivalent inlined version:
ErrorsType[T] extends (...args: infer P) => any ? P : []
This makes the error you mentioned go away. Note that once this is resolved your example code has other errors, but those are out of scope for the question as asked.
Playground link to code

How can I get the keyof a custom type declaration for NodeJS ProcessEnv?

I have added a custom type declaration to index.d.ts file for processEnv as outlined below:
declare global {
namespace NodeJS {
interface ProcessEnv extends IProcessEnv {
SOME_VAR: string;
SOME_OTHER_VAR: string
}
}
I then have a function where I want to check that the string value for the property is on the ProcessEnv which I then attempted to use the keyof but it doesn't seem to validate the string as a valid key on ProcessEnv.
type EnvTypes = keyof NodeJS.ProcessEnv;
const someFunction = (envKey : EnvTypes) => {
...
};
someFunction('MY_VAR'); // This should show as red as its not on the interface
The intellisense works as expected showing the valid options but I am unable to validate the string passed as a valid key. How can this be achieved?

Choosing correct component type based on runtime condition

I am looking for some guidance on how best to accomplish my goal. Essentially, I want to choose the correct component type based on a runtime condition(someCondition). However, I am getting the issue below. I am sure I am overlooking something simple. I imagine I need some factory to produce the correct type(not sure why I am having trouble coming up with one 😅).
I would appreciate it if someone would explain the best way to approach this problem for future reference! Thank you in advance!
Types of construct signatures are incompatible. Type 'new <T>(props: Props<T>) => BComp<T>' is not assignable to type 'new <T>(props: Props<T>) => AComp<T>'. Construct signature return types 'BComp<T>' and 'AComp<T>' are incompatible. The types of 'getState' are incompatible between these types. Type '(s: BState) => BState' is not assignable to type '(s: AState) => AState'. Types of parameters 's' and 's' are incompatible. Property 'b' is missing in type 'AState' but required in type 'BState'.ts(2419) typescript_advance_types.ts(23, 5): 'b' is declared here.
Components:
interface Props<T> {
items: T[];
}
interface ComponentState {}
abstract class Component<T, S extends ComponentState> {
constructor(readonly props: Props<T>){}
abstract getState(s:S):S;
}
interface AState extends ComponentState {
a: boolean;
}
class AComp<T> extends Component<T, AState>{
getState(s: AState): AState {
throw new Error("Method not implemented.");
}
}
interface BState extends ComponentState {
b: boolean;
}
class BComp<T> extends Component<T, BState>{
getState(s: BState): BState {
throw new Error("Method not implemented.");
}
}
Conditionally choosing type:
let someCondition = false;
let component = AComp;
if(someCondition){
component = BComp; // issue
}
export const FinalComponent = component;

Turning map into plain object for TS declaration

I have this:
let cachedPromises: Map<string, Promise<any>> = new Map();
what is the equivalent declaration for a plain object?
Something like this:
interface IMyMap {
[key: string]: Promise<any>
}
let cachedPromises: IMyMap = {};
is that sufficient?
This is sufficient, but it does come as a double edged sword in that you can't implement the interface on a class that has any property that does not return a Promise:
class Person implements IMyMap {
[key: string]: Promise<any>
constructor(private firstName) { // Error: Property 'firstName' of type 'string' is not assignable to string index type 'Promise<any>'.
}
}

Cannot extend base class with Mongoose and Typescript in NodeJS

I'm attempting to use Mongoose 4.5.4 and it's typings in NodeJS with Typescript (obviously) while utilizing the Repository Pattern.
RepositoryBase:
export class RepositoryBase<T extends mongoose.Document> implements IRead<T>, IWrite<T> {
private _model: mongoose.Model<mongoose.Document>;
constructor(schemaModel: mongoose.Model<mongoose.Document>) {
this._model = schemaModel;
}
create(item: T): mongoose.Promise<mongoose.model<T>> {
return this._model.create(item);
}
//......
//Additional CRUD methods excluded for brevity
}
UserRepository:
export class UserRepository extends RepositoryBase<IUserModel> {
constructor() {
super(UserSchema);
}
}
I have the following type error on the call to super() from UserRepository
[ts]
Argument of type '<U>(name: string) => IModelConstructor<U> & EventEmitter' is not assignable to parameter of type 'IModelConstructor<Document> & EventEmitter'.
Type '<U>(name: string) => IModelConstructor<U> & EventEmitter' is not assignable to type 'IModelConstructor<Document>'.
Property 'findById' is missing in type '<U>(name: string) => IModelConstructor<U> & EventEmitter'.
import UserSchema
Does anyone know why? My creation of my model (UserSchema) is very simple:
let model = mongooseConnection.model<IUserModel>("Users", UserSchema.schema);
I would much appreciate a push in the right direction.
Doing a simple export const model = mongooseConnection.model<IUserModel>("Users", UserSchema.schema); was enough to solve this typing issue.

Resources