359

I've a lot of tables in Lovefield and their respective Interfaces for what columns they have.

Example:

export interface IMyTable {
  id: number;
  title: string;
  createdAt: Date;
  isDeleted: boolean;
}

I'd like to have the property names of this interface in an array like this:

const IMyTable = ["id", "title", "createdAt", "isDeleted"];

I cannot make an object/array based on the interface IMyTable directly which should do the trick because I'd be getting the interface names of the tables dynamically. Hence I need to iterate over these properties in the interface and get an array out of it.

How do I achieve this result?

BinaryButterfly
  • 18,137
  • 13
  • 50
  • 91
Tushar Shukla
  • 5,666
  • 2
  • 27
  • 41
  • 1
    This answer is the most correct in my opinion, unfortunately there's not easier way as mentioned: https://stackoverflow.com/questions/43909566/get-keys-of-a-typescript-interface-as-array-of-strings#answer-43922291 – andnik Jul 12 '21 at 13:00

17 Answers17

104

As of TypeScript 2.3 (or should I say 2.4, as in 2.3 this feature contains a bug which has been fixed in typescript@2.4-dev), you can create a custom transformer to achieve what you want to do.

Actually, I have already created such a custom transformer, which enables the following.

https://github.com/kimamula/ts-transformer-keys

import { keys } from 'ts-transformer-keys';

interface Props {
  id: string;
  name: string;
  age: number;
}
const keysOfProps = keys<Props>();

console.log(keysOfProps); // ['id', 'name', 'age']

Unfortunately, custom transformers are currently not so easy to use. You have to use them with the TypeScript transformation API instead of executing tsc command. There is an issue requesting a plugin support for custom transformers.

Eduardo Cuomo
  • 17,828
  • 6
  • 117
  • 94
kimamula
  • 11,427
  • 8
  • 35
  • 29
  • Thanks for your response, i already saw and installed this custom transformer yesterday but since this uses typescript 2.4, this of no use to me as of now. – Tushar Shukla May 12 '17 at 06:08
  • In that thread, someone just added a means to patch typescript and allow custom transformers. https://github.com/nonara/ts-patch. You can (maybe) make it easier to use your custom transformer! – Seph Reed Nov 15 '19 at 17:17
  • 34
    Unfortunately, the package is broken, whatever I do I am always getting `ts_transformer_keys_1.keys is not a function` – fr1sk Jun 01 '20 at 17:47
  • 1
    works well with vite https://github.com/vitejs/vite/discussions/7580#discussioncomment-3099161 – Penguin Aug 10 '22 at 03:15
  • 9
    It might work well, But adding a new npm package just to get Interface keys wouldn't justify the requirement – Guru Vishnu Vardhan Reddy Oct 21 '22 at 12:37
  • do not work (tested in browser) and do not explains the solution. ts_transformer_keys_1.keys is not a function – drdrej Nov 09 '22 at 15:15
  • I can't get this to work with vite, not sure what I'm doing wrong – Andrew Jan 26 '23 at 18:04
68

I faced a similar problem: I had a giant list of properties that I wanted to have both as an interface (compile-time), and an object (run-time) out of it.

NOTE: I didn't want to write (type with keyboard) the properties twice! DRY.


One thing to note here is, interfaces are enforced types at compile-time, while objects are mostly run-time. (Source)

As @derek mentioned in another answer, the common denominator of interface and object can be a class that serves both a type and a value.

So, TL;DR, the following piece of code should satisfy the needs:

class MyTableClass {
    // list the propeties here, ONLY WRITTEN ONCE
    id = "";
    title = "";
    isDeleted = false;
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

// This is the pure interface version, to be used/exported
interface IMyTable extends MyTableClass { };

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

// Props type as an array, to be exported
type MyTablePropsArray = Array<keyof IMyTable>;

// Props array itself!
const propsArray: MyTablePropsArray =
    Object.keys(new MyTableClass()) as MyTablePropsArray;

console.log(propsArray); // prints out  ["id", "title", "isDeleted"]


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

// Example of creating a pure instance as an object
const tableInstance: MyTableClass = { // works properly!
    id: "3",
    title: "hi",
    isDeleted: false,
};

(Here is the above code in Typescript Playground to play more)

PS. If you don't want to assign initial values to the properties in the class, and stay with the type, you can do the constructor trick:

class MyTableClass {
    // list the propeties here, ONLY WRITTEN ONCE
    constructor(
        readonly id?: string,
        readonly title?: string,
        readonly isDeleted?: boolean,
    ) {}
}

console.log(Object.keys(new MyTableClass()));  // prints out  ["id", "title", "isDeleted"] 

Constructor Trick in TypeScript Playground.

Aidin
  • 25,146
  • 8
  • 76
  • 67
  • 1
    The `propsArray` is only accessible when you initialized the keys though. – tlt Apr 12 '20 at 10:53
  • I don't get what you mean by "initialized" @denkquer. In the first example, `propsArray` is available before the `tableInstance` if that's what you mean, so clearly before the initialization of the instance. However, if you are referring to the fake values assigned in `MyTableClass`, those are just there to shortly imply the "type" of the properties. If you don't want them, you can go with the constructor trick in the PS example. – Aidin Apr 13 '20 at 08:23
  • 1
    In my understanding a value is initialized when it has any value. Your "constructor trick" is misleading because you can't just replace the `MyTableClass` with the latter one and expect to receive keys in the `propsArray` as uninitialized vars and types are stripped at runtime. You always have to supply them with some kind of default value. I've found that initializing them with `undefined` is the best approach. – tlt Apr 14 '20 at 10:59
  • @denkquer sorry, my Constructor trick was missing `readonly` and `?` on the paramteres. I just updated that. Thanks for pointing out. Now I think it works the way you said is misleading and cannot work. :) – Aidin Apr 15 '20 at 22:03
  • why you need to this much of things? since you initialise your class when it define you can simply use `console.log(Object.keys(new MyTableClass()))` it will do the job – Krishantha Dinesh Nov 07 '22 at 16:47
  • @KrishanthaDinesh we need properties during compile-time (TypeScript and type checking in IDE) not during run-time (JavaScript). – Aidin Nov 09 '22 at 06:25
65

Maybe it's too late, but in version 2.1 of TypeScript you can use keyof to get the type like this:

interface Person {
    name: string;
    age: number;
    location: string;
}

type K1 = keyof Person; // "name" | "age" | "location"
type K2 = keyof Person[];  // "length" | "push" | "pop" | "concat" | ...
type K3 = keyof { [x: string]: Person };  // string

Source: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html#keyof-and-lookup-types

nzz
  • 5
  • 1
  • 2
Nathan Gabriel
  • 1,662
  • 6
  • 9
  • 5
    Thanks for the answer but not sure if it helps someone to use statically created Types from interface. IMHO, we can use interfaces/types interchangeably in most of the cases. Plus this would require manual creation of types for multiple interfaces. However the solution looks good if someone just needs to get types out of an interface. – Tushar Shukla Jun 22 '20 at 04:26
  • 251
    I downvoted because this does not solve the question as stated. The goal is to get a list of `string[]` out of a type; not to get the key-type with `keyof MyType`. – Charles Capps Jun 01 '21 at 23:04
  • 3
    @CharlesCapps this can be easily solved by wrapping keyof as in `type K2 = (keyof Person)[]` – Andrei Sirotin Nov 12 '21 at 11:58
  • 2
    type K2 = (keyof Person)[] did not compile forme @AndreiSirotin. Any way to get the array of strings? – Dean Hiller Mar 06 '22 at 01:39
  • 12
    @CharlesCapps `type K2 = (keyof Person)[]` is still a type, not an array. – Suncat2000 May 05 '22 at 19:26
  • 2
    I agree this does not answer the main question. It returns a type and not a value, so it will be lost during compilation. It is great if you want some more static validation, but you cannot do any runtime check with this. The question was about parsing those values into a runtime variable, not deriving a new type. – KawaLo Jul 31 '22 at 20:25
  • It only allow you to create a type, I want to create an array – WestMountain Feb 24 '23 at 11:21
52

The following requires you to list the keys on your own, but at least TypeScript will enforce IUserProfile and IUserProfileKeys have the exact same keys (Required<T> was added in TypeScript 2.8):

export interface IUserProfile  {
  id: string;
  name: string;
};
type KeysEnum<T> = { [P in keyof Required<T>]: true };
const IUserProfileKeys: KeysEnum<IUserProfile> = {
  id: true,
  name: true,
};
Maciek Wawro
  • 844
  • 6
  • 7
  • 3
    Pretty cool trick. Now it's easy to enforce implementing all keys of `IUserProfile` and would be easy to extract them from the const `IUserProfileKeys`. This is exactly what I've been looking for. No need to convert all my interfaces to classes now. – Anddo Jul 29 '20 at 07:58
  • 5
    I downvoted as agin it is not "interface as array of strings" that I want to get from type or interface. I do not want to get type, I know how to do it. I think it is shortly speeking, how to convert type to variable. – Konrad Grzyb Mar 14 '22 at 11:54
  • 1
    That's kind of halfway there. A type can be created from an object, but an object cannot be created from a type. – Suncat2000 May 05 '22 at 19:29
37

Safe variants

Creating an array or tuple of keys from an interface with safety compile-time checks requires a bit of creativity. Types are erased at run-time and object types (unordered, named) cannot be converted to tuple types (ordered, unnamed) without resorting to non-supported techniques.

Comparison to other answers

The here proposed variants all consider/trigger a compile error in case of duplicate or missing tuple items given a reference object type like IMyTable. For example declaring an array type of (keyof IMyTable)[] cannot catch these errors.

In addition, they don't require a specific library (last variant uses ts-morph, which I would consider a generic compiler wrapper), emit a tuple type as opposed to an object (only first solution creates an array) or wide array type (compare to these answers) and lastly don't need classes.

Variant 1: Simple typed array

// Record type ensures, we have no double or missing keys, values can be neglected
function createKeys(keyRecord: Record<keyof IMyTable, any>): (keyof IMyTable)[] {
  return Object.keys(keyRecord) as any
}

const keys = createKeys({ isDeleted: 1, createdAt: 1, title: 1, id: 1 })
// const keys: ("id" | "title" | "createdAt" | "isDeleted")[]

+ easiest +- manual with auto-completion - array, no tuple

Playground

If you don't like creating a record, take a look at this alternative with Set and assertion types.


Variant 2: Tuple with helper function

function createKeys<T extends readonly (keyof IMyTable)[] | [keyof IMyTable]>(
    t: T & CheckMissing<T, IMyTable> & CheckDuplicate<T>): T {
    return t
}

+ tuple +- manual with auto-completion +- more advanced, complex types

Playground

Explanation

createKeys does compile-time checks by merging the function parameter type with additional assertion types, that emit an error for not suitable input. (keyof IMyTable)[] | [keyof IMyTable] is a "black magic" way to force inference of a tuple instead of an array from the callee side. Alternatively, you can use const assertions / as const from caller side.

CheckMissing checks, if T misses keys from U:

type CheckMissing<T extends readonly any[], U extends Record<string, any>> = {
    [K in keyof U]: K extends T[number] ? never : K
}[keyof U] extends never ? T : T & "Error: missing keys"

type T1 = CheckMissing<["p1"], {p1:any, p2:any}> //["p1"] & "Error: missing keys"
type T2 = CheckMissing<["p1", "p2"], { p1: any, p2: any }> // ["p1", "p2"]

Note: T & "Error: missing keys" is just for nice IDE errors. You could also write never. CheckDuplicates checks double tuple items:

type CheckDuplicate<T extends readonly any[]> = {
    [P1 in keyof T]: "_flag_" extends
    { [P2 in keyof T]: P2 extends P1 ? never :
        T[P2] extends T[P1] ? "_flag_" : never }[keyof T] ?
    [T[P1], "Error: duplicate"] : T[P1]
}

type T3 = CheckDuplicate<[1, 2, 3]> // [1, 2, 3]
type T4 = CheckDuplicate<[1, 2, 1]> 
// [[1, "Error: duplicate"], 2, [1, "Error: duplicate"]]

Note: More infos on unique item checks in tuples are in this post. With TS 4.1, we also can name missing keys in the error string - take a look at this Playground.


Variant 3: Recursive type

With version 4.1, TypeScript officially supports conditional recursive types, which can be potentially used here as well. Though, the type computation is expensive due to combinatory complexity - performance degrades massively for more than 5-6 items. I list this alternative for completeness (Playground):

type Prepend<T, U extends any[]> = [T, ...U] // TS 4.0 variadic tuples

type Keys<T extends Record<string, any>> = Keys_<T, []>
type Keys_<T extends Record<string, any>, U extends PropertyKey[]> =
  {
    [P in keyof T]: {} extends Omit<T, P> ? [P] : Prepend<P, Keys_<Omit<T, P>, U>>
  }[keyof T]

const t1: Keys<IMyTable> = ["createdAt", "isDeleted", "id", "title"] // ✔

+ tuple +- manual with auto-completion + no helper function -- performance


Variant 4: Code generator / TS compiler API

ts-morph is chosen here, as it is a tad simpler wrapper alternative to the original TS compiler API. Of course, you can also use the compiler API directly. Let's look at the generator code:

// ./src/mybuildstep.ts
import {Project, VariableDeclarationKind, InterfaceDeclaration } from "ts-morph";

const project = new Project();
// source file with IMyTable interface
const sourceFile = project.addSourceFileAtPath("./src/IMyTable.ts"); 
// target file to write the keys string array to
const destFile = project.createSourceFile("./src/generated/IMyTable-keys.ts", "", {
  overwrite: true // overwrite if exists
}); 

function createKeys(node: InterfaceDeclaration) {
  const allKeys = node.getProperties().map(p => p.getName());
  destFile.addVariableStatement({
    declarationKind: VariableDeclarationKind.Const,
    declarations: [{
        name: "keys",
        initializer: writer =>
          writer.write(`${JSON.stringify(allKeys)} as const`)
    }]
  });
}

createKeys(sourceFile.getInterface("IMyTable")!);
destFile.saveSync(); // flush all changes and write to disk

After we compile and run this file with tsc && node dist/mybuildstep.js, a file ./src/generated/IMyTable-keys.ts with following content is generated:

// ./src/generated/IMyTable-keys.ts
const keys = ["id","title","createdAt","isDeleted"] as const;

+ auto-generating solution + scalable for multiple properties + no helper function + tuple - extra build-step - needs familiarity with compiler API

ford04
  • 66,267
  • 20
  • 199
  • 171
  • 1
    Looks like variant 1 doesn't do any work because I'll have to type out the whole keys in createKeys. – Obinna Nnenanya Mar 22 '22 at 16:45
  • Variant 1 still statically enforces the resulting array to be complete. That said, I struggle to make variant 2 generic over `U` instead of `IMyTable`. – FichteFoll Jan 19 '23 at 19:11
25

This should work

var IMyTable: Array<keyof IMyTable> = ["id", "title", "createdAt", "isDeleted"];

or

var IMyTable: (keyof IMyTable)[] = ["id", "title", "createdAt", "isDeleted"];
Damathryx
  • 2,706
  • 3
  • 25
  • 48
  • 43
    Not that it's wrong, but to be clear here you are just "enforcing the values of the array" to be correct. The developer still needs to write them down twice, manually. – Aidin Jan 19 '20 at 20:52
  • 19
    This won't prevent key duplicates or missing keys. Like `var IMyTable: Array = ["id", "createdAt", "id"];` – ford04 Mar 30 '20 at 12:32
17

Instead of defining IMyTable as in interface, try defining it as a class. In typescript you can use a class like an interface.

So for your example, define/generate your class like this:

export class IMyTable {
    constructor(
        public id = '',
        public title = '',
        public createdAt: Date = null,
        public isDeleted = false
    )
}

Use it as an interface:

export class SomeTable implements IMyTable {
    ...
}

Get keys:

const keys = Object.keys(new IMyTable());
Derek
  • 967
  • 1
  • 9
  • 16
5

You will need to make a class that implements your interface, instantiate it and then use Object.keys(yourObject) to get the properties.

export class YourClass implements IMyTable {
    ...
}

then

let yourObject:YourClass = new YourClass();
Object.keys(yourObject).forEach((...) => { ... });
Dan Def
  • 1,836
  • 2
  • 21
  • 39
  • 2
    Doesn't work in my case, i'd have to list those properties of the interface but that is not what i want? Name of interface comes dynamically and then i've to determine its properties – Tushar Shukla May 11 '17 at 08:33
  • This produces an error (v2.8.3): `Cannot extend an interface […]. Did you mean 'implements'?` However, using `implements` instead requires manually copying the interface, which is exactly what I don't want. – Jakob Jingleheimer May 06 '18 at 11:44
  • @jacob sorry, it should have been `implements` and I have updated my answer. As @basarat has stated, interfaces don't exist at runtime so the only way is to implement it as a class. – Dan Def May 06 '18 at 20:04
  • You mean instead of an interface use a class? Unfortunately I cannot as the interface comes from a 3rd-party (`@types/react`). I manually copied them, but that's hardly future-proof I'm trying to dynamically bind non-lifecycle methods (which are already bound), but they're not declared on React.Component (the class). – Jakob Jingleheimer May 06 '18 at 20:41
  • No, I mean create a class that implements your 3rd party interface and get the properties of that class at runtime. – Dan Def May 08 '18 at 11:18
  • @DanDef that does not work: The implementing class has no properties and `Object.keys` returns an empty array. When the methods are not optional (moot point because they are in the 3rd-party interface), it causes a `Class incorrectly implements interface.` error https://www.typescriptlang.org/play/index.html#src=interface%20ISome%20%7B%0D%0A%09methodA%3F(foo%3A%20any)%3A%20string%3B%0D%0A%09methodB%3F(qux%3A%20any)%3A%20number%3B%0D%0A%7D%0D%0A%0D%0Aclass%20Some%20implements%20ISome%20%7B%7D%0D%0Aconst%20someMethods%20%3D%20Object.keys(new%20Some)%3B%0D%0A%0D%0Aconsole.log(someMethods)%3B – Jakob Jingleheimer May 19 '18 at 22:50
5

This was a tough one! Thank you, everyone, for your assistance.

My need was to get keys of an interface as an array of strings to simplify mocha/chai scripting. Not concerned about using in the app (yet), so didn't need the ts files to be created. Thanks to ford04 for the assistance, his solution above was a huge help and it works perfectly, NO compiler hacks. Here's the modified code:

Option 2: Code generator based on TS compiler API (ts-morph)

Node Module

npm install --save-dev ts-morph

keys.ts

NOTE: this assumes all ts files are located in the root of ./src and there are no subfolders, adjust accordingly

import {
  Project,
  VariableDeclarationKind,
  InterfaceDeclaration,
} from "ts-morph";

// initName is name of the interface file below the root, ./src is considered the root
const Keys = (intName: string): string[] => {
  const project = new Project();
  const sourceFile = project.addSourceFileAtPath(`./src/${intName}.ts`);
  const node = sourceFile.getInterface(intName)!;
  const allKeys = node.getProperties().map((p) => p.getName());

  return allKeys;
};

export default Keys;

usage

import keys from "./keys";

const myKeys = keys("MyInterface") //ts file name without extension

console.log(myKeys)
Tushar Shukla
  • 5,666
  • 2
  • 27
  • 41
Michael
  • 484
  • 6
  • 8
4

Some people have suggested this, which has the benefit of being the most simple solution:

const properties: (keyof IMyTable)[] = ["id", "title", "createdAt", "isDeleted"];

However while this adds some type-safety (we cannot use non-existing properties by mistake), it is not a fully safe solution because we could miss some properties and have duplicates. So I have fixed that, this verbose solution is fully type-safe and prevents inconsistencies between the compile-time types and the run-time values of the array:

const properties: [
    keyof Pick<IMyTable, 'id'>,
    keyof Pick<IMyTable, 'title'>,
    keyof Pick<IMyTable, 'createdAt'>,
    keyof Pick<IMyTable, 'isDeleted'>
] = ['id', 'title', 'createdAt', 'isDeleted'];

Of course this is only applicable if you are fine with NOT avoiding the repetition, but at least you only have to be sure that you write all properties correctly once (on the Pick type util), and the rest will always raise an error if there's any mistake. I think it's the most robust solution among the simple, easy to understand and readable solutions.

cprcrack
  • 17,118
  • 7
  • 88
  • 91
4

In this blog

Getting a type from our array

Now we can get a type from our animals array using typeof:

const animals = ['cat', 'dog', 'mouse'] as const
type Animal = typeof animals[number]

// type Animal = 'cat' | 'dog' | 'mouse'
edalvb
  • 581
  • 6
  • 7
3

As others have already said, the types are not available at runtime, so you need to either generate them or write by hand.

If you write them by hand, then the simple Array<keyof IMyTable> does not verify missing keys.

Here is a really great answer that answers the problem of declaring the array of keys with type safety. (Credit goes to him.) Relevant code:

export interface IMyTable {
    id: number;
    title: string;
    createdAt: Date;
    isDeleted: boolean;
}

type Invalid<T> = ['Needs to be all of', T];
const arrayOfAll =
    <T>() =>
    <U extends T[]>(
        ...array: U & ([T] extends [U[number]] ? unknown : Invalid<T>[])
    ) =>
        array;

const fields = arrayOfAll<keyof IMyTable>()(
    'id',
    'createdAt',
    'title',
    'isDeleted',
);

If there were missing fields, an error would be shown.

andras
  • 3,305
  • 5
  • 30
  • 45
1

If you can't use a custom transformer (or prefer not to), I think the best way is what I'm about to show, which has the following advantages:

  1. it allows for "semi-automatic" population of the array (at least in VS Code);
  2. it results in an array that TypeScript recognizes as having elements that are a union of the interface's keys;
  3. it doesn't involve performance-degrading recursive tricks.

Here is the approach:

interface Foo {
  fizz?: string;
  buzz: number;
}

const FooKeysEnum: { [K in keyof Required<Foo>]: K } = {
  fizz: 'fizz',
  buzz: 'buzz',
};

const FooKeys = Object.values(FooKeysEnum);

The "semi-automatic" population of the array in VS Code comes from the fact that when FooKeysEnum has a red underline because it's missing properties, you can hover over it and select "Add missing properties" from the "Quick Fix" menu. (This benefit is shared by other approaches already shown in this thread, but I don't think anyone had mentioned it yet. ETA: I was mistaken here; auto-completion has been mentioned elsewhere in the thread.)

Finally, by using Object.values() instead of Object.keys() to create the array, you get TypeScript to recognize that FooKeys has the type ("fizz" | "buzz")[]. It doesn't know that FooKeys[0] is "fizz" and that FooKeys[1] is "buzz", but still, better than the string[] you get with Object.keys().

EDIT:

In VS Code, you can also set up a keyboard shortcut in keybindings.json for executing a "Quick Fix," which makes it even quicker to trigger Add missing properties. Would look something like this:

{
  "key": "shift+cmd+a",
  "command": "editor.action.codeAction",
  "args": {
    "kind": "quickfix",
    "apply": "ifSingle"
  }
}

Then if something's got a red underline, you can click it and use the keyboard-shortcut, and if there's only one quick-fix option available then it will run. Would be great if there were a way to target specific quick-fixes, and even better if it could be done automatically on file-save, but I don't think that's possible at the time of writing.

0

Here it is. Live Link

export interface Identity {
  username: string;
  name: string;
  surname: string;
}

const extractedKeys: Array<keyof Identity> = ['username', 'name', 'surname']
console.log(extractedKeys)
Akib
  • 304
  • 2
  • 9
-1

The problem is that types are not available at runtime. I ended up defining an object and deriving the type from that object. You still get type support and gain the ability to get a list of keys all from a single place.

const myTable =  {
  id: 0,
  title: '',
  createdAt: null as Date,
  isDeleted: false,
};

export type TableType = typeof myTable;
export type TableTypeKeys = (keyof TableType)[];
export const tableKeys: TableTypeKeys = Object.keys(
  myTable
) as TableTypeKeys;

Implicit types resulting from above will be (you can check with VSCode or another quality IDE):

type TableType = {
    id: number;
    title: string;
    createdAt: Date;
    isDeleted: boolean;
}

type TableTypeKeys = ("id" | "title" | "createdAt" | "isDeleted")[]

and at runtime you'll be able to access the tableKeys array

console.log(tableKeys);
// output: ["id" , "title" , "createdAt" , "isDeleted"]
Alex
  • 9,250
  • 11
  • 70
  • 81
-3

Can't. Interfaces don't exist at runtime.

A workaround:

Create a variable of the type and use Object.keys on it

Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92
basarat
  • 261,912
  • 58
  • 460
  • 511
  • 1
    Do you mean like this: `var abc: IMyTable = {}; Object.keys(abc).forEach((key) => {console.log(key)});` – Tushar Shukla May 11 '17 at 08:18
  • 5
    Nope, because that object has no keys on it. An interface is something that TypeScript uses but evaporates in the JavaScript, so there's no information left to inform any "reflection" or "interspection". All JavaScript knows is that there's an empty object literal. Your only hope is to wait for (or [request that](https://github.com/Microsoft/TypeScript/issues)) TypeScript includes a way to generate an array or object with all the keys in the interface into the source code. Or, as Dan Def says, if you can use a class, you will have the keys defined in the form of properties in every instance.. – Jesper May 11 '17 at 10:03
  • 1
    You would also get an error by TypeScript on this line: `var abc: IMyTable = {}` because the empty object literal does not conform to the shape of that interface. – Jesper May 11 '17 at 10:05
  • 28
    If this does not work, why there are upvotes on this answer? – dawez Jun 18 '19 at 15:50
  • Well, there are upvotes because it works, indeed. `var abc: IMyTable = {};` produces a compilation error if `IMyTable` contains any key. So you have to insert all the keys required by the `IMyTable` in the object literal. The only (possible) drawback is that you could insert extra keys and TypeScript would not really complain. – Alberto Chiesa Sep 26 '19 at 07:35
  • 3
    downvote reason: no mention that it does not work for nullable values – TamusJRoyce Oct 31 '19 at 19:18
  • 4
    It's ultimately not a great solution because you have to supply values. Probably better off just keeping a list of keys. – Daniel Thompson Feb 21 '20 at 23:15
  • This doesn't work for interfaces as the question asks. Optional keys in the interface will not be instantiated, meaning this won't have _all_ values. – Ben Winding May 08 '20 at 02:44
-9
// declarations.d.ts
export interface IMyTable {
      id: number;
      title: string;
      createdAt: Date;
      isDeleted: boolean
}
declare var Tes: IMyTable;
// call in annother page
console.log(Tes.id);
  • 1
    This code will not work as the typescript syntax is not available on run-time. If you check this code on typescript playground then you'll notice that the only thing that compiles to JavaScript is `console.log(Tes.id)` which of course would be error 'Uncaught ReferenceError: Tes is not defined' – Tushar Shukla Oct 03 '19 at 08:10