I ran into a problem with decrypting a token in a project, I want to extract data from the incoming token, compare it with the data from the database, and then perform certain actions.
The problem is that when I get the payload from "jwtService.decode()", I can't access the "decodedJwt.email" field, nest complains that "decodedJwt" is not an object. But if you return typeof "decodedJwt" then the answer is a string. However, I cannot access the "email" field in the code itself. If I return "decodedJwt" in this function, then in postman I will get the very necessary object with the necessary fields, including the same "email" field. What's my mistake?
Reply from nest:
Property 'email' does not exist on type 'string | { [key: string]: any; }'.
Property "email" does not exist on type "string".ts(2339)
async refreshTokenByOldToken(authHeader: string) {
const decodedJwt = this.jwtService.decode(authHeader.split(' ')[1])
return decodedJwt.email
}
You need to cast the type. Tell explicitly what type it is.
type PayloadType = {
email: string;
}
async refreshTokenByOldToken(authHeader: string) {
const decodedJwt = this.jwtService.decode(authHeader.split(' ')[1]) as PayloadType;
return decodedJwt.email
}
You can reference PayloadType other places as well for example in your service instead of string | { [key: string]: any; } you can have string | PayloadType.
examples of type casting
because your decodedJwt string is different data type. You access email information two cases;
If you #nestjs/jwt package
const decoded: JwtPayload = this.jwtService.decode(yourAccessToken);
const email = decoded.email;
OR
Base 64 Converting
const base64Payload = authHeader.split(" ")[1];
const payloadBuffer = Buffer.from(base64Payload, "base64");
const updatedJwtPayload: JwtPayload = JSON.parse(payloadBuffer.toString()) as JwtPayload;
const email = updatedJwtPayload.email;
Related
I am new in TS world and I was wondering if it's possible to make a request to an API end point which returns an object with a structure which I don't know.
Can TS help me with the properties in advance with autocompletion?
If you don't know what the response object structure will be, you can use unknown as a basis and afterwards check the type of the result.
const result = (await response.json()) as unknown;
You will need a list of all the possible types you expect the response to be if you want autocompletion to work, because TS needs some definition of what the object could be.
Let's say I expect one of two types:
interface Person {
id: number
name: string;
age: number;
}
and
interface Message {
id: number
text: string;
sender: string;
receiver: string;
}
You can use type guards to check what the actual result is. First you have to create your custom type guards, like this:
const isPerson = (person: Person | unknown): person is Person =>
!!(person as Person)?.id || !!(person as Person)?.name || !!(person as Person)?.age;
and
const isMessage = (message: Message | unknown): message is Message =>
!!(message as Message)?.id || !!(message as Message)?.text || !!(message as Message)?.sender || !!(message as Message)?.receiver;
Now you can use these type guards to know if the result is one of these defined types:
if (!!isPerson(result)) {
// result is of type Person and autocompletion will work with properties of type Person
} else if (!!isMessage(result)) {
// result is of type Message and autocompletion will work with properties of type Message
} else {
// result is of type unknown and further checks will need to done
}
I am consuming an api that returns nested json. It is dynamic so the keys will often be different, but can only be an object or a string, cannot be an array or number etc.
Here is an example:
const response = {
name: 'Pete',
location: 'London',
age: {
year: '21'
}
}
I have tried type it like this:
type Response = {
[key: string]: string
}
Then in a React component I want to use it like this:
type Response = {
[key: string]: string
}
const Foo = ({ data }: Response) => {
return <pre>{JSON.stringify(data, null, 2)}</pre>
}
This gives me the following error:
Property 'x' does not exist on type 'string'
Can anyone point me in the right direction here?
here is a link to stackblitz - https://stackblitz.com/edit/react-ts-4n7vcy?file=App.tsx
What you are doing is assigning the type Response to the props argument of your Foo functional component. So actually, the Foo component accepts a props object with the type of Response, but the data variable is inferred as a string by typescript. What you should do instead is;
interface IFooProps{
data: Response;
}
const Foo = (props: IFooProps) => {
return <pre>{JSON.stringify(props.data, null, 2)}</pre>;
};
The issue is that the values in your type are strings. When you extracted the data key its type was a string. You either need to type it like:
type Response = {
data: {
[key: string]: string
}
}
Or accept it in the component like
const Foo = (data: Response) => {
// ...
}
If you want the values to be either string or nested objects, you can do:
type Response = {
[key: string]: string | Response
}
And then the data in your example will be of type string | Response.
I'v faced a problem trying to define an interface for the following structure:
interface JSONRecord {
[propName: string]: any;
}
type ReturnType = (id: string|number, field: string, record: JSONRecord) => string
export const formatDictionary = ({
mode = "render", key = "originalValue",
defaultKey = "originalValue"
}):ReturnType => (id, field, record) => {
...
}
interface Lookup {
Dictionary: ({mode, key, defaultKey}:{mode: string, key: string, defaultKey: string}) => ReturnType,
...
}
export const functionLookup:Lookup = {
Dictionary: formatDictionary,
...
}
export const formatField = (params:JSONRecord):string|ReturnType => {
const type:string = params.type
if (type === undefined) { return identity }
const fn = functionLookup[type]
if (fn === undefined) { return identity }
return fn({ ...params })
}
I'm getting the following errors:
In line const fn = functionLookup[type] : Element implicitly has an 'any' type becasue expression of type string can't be used to index type 'Lookup'. No index signature with parameter of type 'string' was found on type 'Lookup'.
I'm not really sure why is this happening, i thought that the Dictionary i defined in Lookup is supposed to be interpreted as a string. When i change Dictionary to [x: string]: ({mode, key, defaultKey}:{mode: string, key: string, defaultKey: string}) => ReturnType the error disappears, but i want to be specific with the arguments that can be passed.
In line return fn({ ...params }) : Expected 3 arguments, but got 1
I can't really wrap my head around this, the function is clearly expecting only 1 object as an argument {mode, key, defaultKey} or is it expecting the ReturnType function there?
I would appreciate any help. Thanks a lot in advance :)
In you case (from sandbox):
const anExampleVariable = "Hello World"
console.log(anExampleVariable)
// To learn more about the language, click above in "Examples" or "What's New".
// Otherwise, get started by removing these comments and the world is your playground.
interface Lookup {
test: number
}
const functionLookup:Lookup = {
test: 5
}
const params = {
type: 'test'
};
const type = params.type
const a = functionLookup[type]
params variable is infered as {type: string}.
Here functionLookup[type] you want use type as index for functionLookup, but TS does not work that way. Because you can't just use general type string as index for Lookup type.
Lookup allows you to use only literal test as index.
So you can add as const prefix to your params vvariable.
const params = {
type: 'test'
} as const;
You can make Lookup indexed:
interface Lookup {
test: number,
[prop:string]:number
}
Or, you can explicitly define a Record type for params:
const params:Record<string, keyof Lookup> = {
type: 'test'
}
In my project I am using Angular LocalStorage to store value of the record which is of Type Filename. I am getting below error in back method
Error
Type 'string | null' is not assignable to type 'FileName | undefined'.
Type 'null' is not assignable to type 'FileName | undefined'.
I need help to solve this error, below is my code
Code
export interface FileName {
fname: number;
sname: number;
Tange: number;
quick: string;
Mark: string;
mine: number;
}
currentName: FileName | undefined = undefined;
previousName: FileName | undefined = undefined;
data(rec: FileName, Mark: HTMLTableDataCellElement) {
const { fname, sname, Tange, record_id, mine } = rec;
const quick = Mark.innerText;
this.name.replaceAndNew({sname, Tange, record_id, quick, mine, fname}).subscribe(data => {
this.process(data);
})
localStorage.setItem('FileName', JSON.stringify(rec));
}
back(){
localStorage.getItem('FileName');
this.currentName = localStorage.getItem('FileName'); -----------------> Error
}
fortunee is correct, however since everything in localStorage is stored as strings, Typescript has no way of guaranteeing that the object is still the same object. In this case, you need to tell Typescript what type the parsed object will be with a cast.
this.currentName = JSON.parse(localStorage.getItem('FileName') || '{}') as FileName;
Afterwards you may want to duck type to make sure that this.currentName isn't an empty object (this could've happened if the user cleared their localStorage).
Example:
if (this.currentName.fname == null) {
// Give the user an error message or something...
this.currentName = undefined;
}
in case there's an error when parsing an object you can use try-catch to set the default value for it
try {
this.currentName = JSON.parse(localStorage.getItem('FileName')) as FileName;
} catch(e){
this.currentName = {}
}
You need to parse it back to an object with JSON.parse
this.currentName = JSON.parse(localStorage.getItem('FileName'));
I run into the same error and just fixed it by:
const currentName = String(localStorage.getItem('FileName') || '');
I'm receiving the following in an API response:
{ "roles": [ "ADMIN", "USER" ] }
where the response will always contain an array of roles (USER, PRESENTER, ORGANIZER, and ADMIN).
I want to convert it into a valid TypeScript array (Role[]), where the type Role is defined as follows:
export type Role = 'USER' | 'PRESENTER' | 'ORGANIZER' | 'ADMIN'
Any ideas?
Your Role type is not an enum. It is just a string type limited to certain values.
You can just cast the result as a Role[] and TypeScript will be happy. This assumes the incoming data never has a bad value!
const data: {roles: Role[]} = JSON.parse('{"roles": ["ADMIN", "USER"]}');
data.roles // TypeScript knows it is a Role[]
You can just cast it to your union type:
const apiRoleArray = ["ADMIN", "USER"];
const realRoleArray: Role[] = <Role[]>apiRoleArray;
BUT you probably want to validate its contents rather than just trusting the API. :-) Drawing on this question's answers, you can create the type by using the keys of an object rather than defining it literally (see the accepted answer there for why):
const roleStrings = {
USER: "",
PRESENTER: "",
ORGANIZER: "",
ADMIN: ""
};
export type Role = keyof typeof roleStrings;
then give yourself a validation function:
const isRole = (s: string): s is Role => {
return roleStrings.hasOwnProperty(s);
};
then a robust conversion function, for example:
const rawToRoleArray = (rawArray: string[]): Role[] => {
return rawArray.map(s => {
if (!isRole(s)) {
throw new Error("Invalid Role: " + s);
}
return <Role>s;
});
};
(you could combine those if you don't need them separately)
then use it:
// Valid
const realRoleArray: Role[] = rawToRoleArray(["ADMIN", "USER"]);
console.log(realRoleArray);
// Invalid
const realRoleArray2: Role[] = rawToRoleArray(["ADMIN", "FOO"]);
console.log(realRoleArray2);
Live in the playground | Live on jsFiddle
If I got you corectly thats what you want to do.
enum RoleEnum {
USER,
PRESENTER,
ORGANIZER,
ADMIN
}
const parseEnum = (name: String): RoleEnum => RoleEnum[`${name}`]
const parsed: RoleEnum[] = [ 'ADMIN', 'USER' ].map(parseEnum)
console.log(parsed)