I have an interface in TypeScript.
interface Employee{
id: number;
name: string;
salary: number;
}
I would like to make salary
as a nullable field (Like we can do in C#). Is this possible to do in TypeScript?
I have an interface in TypeScript.
interface Employee{
id: number;
name: string;
salary: number;
}
I would like to make salary
as a nullable field (Like we can do in C#). Is this possible to do in TypeScript?
All fields in JavaScript (and in TypeScript) can have the value null
or undefined
.
You can make the field optional which is different from nullable.
interface Employee1 {
name: string;
salary: number;
}
var a: Employee1 = { name: 'Bob', salary: 40000 }; // OK
var b: Employee1 = { name: 'Bob' }; // Not OK, you must have 'salary'
var c: Employee1 = { name: 'Bob', salary: undefined }; // OK
var d: Employee1 = { name: null, salary: undefined }; // OK
// OK
class SomeEmployeeA implements Employee1 {
public name = 'Bob';
public salary = 40000;
}
// Not OK: Must have 'salary'
class SomeEmployeeB implements Employee1 {
public name: string;
}
Compare with:
interface Employee2 {
name: string;
salary?: number;
}
var a: Employee2 = { name: 'Bob', salary: 40000 }; // OK
var b: Employee2 = { name: 'Bob' }; // OK
var c: Employee2 = { name: 'Bob', salary: undefined }; // OK
var d: Employee2 = { name: null, salary: 'bob' }; // Not OK, salary must be a number
// OK, but doesn't make too much sense
class SomeEmployeeA implements Employee2 {
public name = 'Bob';
}
"strict" : false
–
Sargasso salary:number|null;
If you do salary?:number; salary = null;
You will get an error. However, salary = undefined;
will work just fine in this case. Solution: use Union i.e. '|' –
Seltzer | null
. You say yourself that undefined isn't the same as null. Optional is undefined, so this doesn't the answer at all? –
Isotone null
if "strictNullChecks": true
is set (e.g. in the tsConfig.json
). –
Longshoreman To be more C# like, define the Nullable
type like this:
type Nullable<T> = T | null;
interface Employee{
id: number;
name: string;
salary: Nullable<number>;
}
Bonus:
To make Nullable
behave like a built in Typescript type, define it in a global.d.ts
definition file in the root source folder. This path worked for me: /src/global.d.ts
emp: Partial<Employee>
, we can do emp.id
or emp.name
etc but if we have emp: Nullable<Employee>
, we can't do emp.id
–
Tot emp
is potentially nullable, the id
property might be invalid. To make the code robust, you should probably use an if
block to check for null first, like this: if (emp) { console.log(emp.id); }
If you use such an if
-block, the TypeScript compiler and the editor "see" that the object inside the block is not null and thus will not generate errors and allow auto completion inside the if-block. (It works well in my Visual Studio 2019 editor and I assume it will work in Visual Studio Code too. But I don't know about other editors.) –
Penury emp: Partial<Employee>
, the resulting type contains all properties from Employee
, but those properties will be nullable. (Well, undefined
able might be the more appropriate term here.) So all properties of emp
are available, but nullable. When using emp: Nullable<Employee>
, the emp
variable itself is nullable. If it is not null, it should be a valid full Employee
instance. You could combine those as well: emp: Nullable<Partial<Employee>>
. In that case, emp
is nullable itself, but when not null, its properties can all be nullable as well. –
Penury type _Nullable<T> = T | null | undefined;
? That's the vuefire definition –
Trilley Union type is in my mind best option in this case:
interface Employee{
id: number;
name: string;
salary: number | null;
}
// Both cases are valid
let employe1: Employee = { id: 1, name: 'John', salary: 100 };
let employe2: Employee = { id: 1, name: 'John', salary: null };
EDIT : For this to work as expected, you should enable the strictNullChecks
in tsconfig
.
optional property
of an interface is declared with a ?
on the property name. salary?: number
means that salary
can be omitted, or equivalently given value undefined
, but cannot give it value null
. Good demonstration of different declarations using optional and/or null. –
Implement salary: number | null
or salary: number | undefined
is still great, because it forces you do set it to something, even if that something is undefined
. It is easy to forget otherwise. –
Hypabyssal Just add a question mark ?
to the optional field.
interface Employee{
id: number;
name: string;
salary?: number;
}
You can just implement a user-defined type like the following:
type Nullable<T> = T | undefined | null;
var foo: Nullable<number> = 10; // ok
var bar: Nullable<number> = true; // type 'true' is not assignable to type 'Nullable<number>'
var baz: Nullable<number> = null; // ok
var arr1: Nullable<Array<number>> = [1,2]; // ok
var obj: Nullable<Object> = {}; // ok
// Type 'number[]' is not assignable to type 'string[]'.
// Type 'number' is not assignable to type 'string'
var arr2: Nullable<Array<string>> = [1,2];
type Nullable<T> = {
[P in keyof T]: T[P] | null;
};
and then u can use it
Nullable<Employee>
This way you can still use Employee
interface as it is somewhere else
type WithNullableFields<T, Fields> = {
[K in keyof T]: K extends Fields
? T[K] | null | undefined
: T[K]
}
let employeeWithNullableSalary: WithNullableFields<Employee, "salary"> = {
id: 1,
name: "John",
salary: null
}
Or you can turn off strictNullChecks ;)
And the reversed version:
type WithNonNullableFields<T, Fields> = {
[K in keyof T]: K extends Fields
? NonNullable<T[K]>
: T[K]
}
Nullable type can invoke runtime error.
So I think it's good to use a compiler option --strictNullChecks
and declare number | null
as type. also in case of nested function, although input type is null, compiler can not know what it could break, so I recommend use !
(exclamination mark).
function broken(name: string | null): string {
function postfix(epithet: string) {
return name.charAt(0) + '. the ' + epithet; // error, 'name' is possibly null
}
name = name || "Bob";
return postfix("great");
}
function fixed(name: string | null): string {
function postfix(epithet: string) {
return name!.charAt(0) + '. the ' + epithet; // ok
}
name = name || "Bob";
return postfix("great");
}
Reference. https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-guards-and-type-assertions
??
. This is less likely to result in accidental errors than the "falsey" logical-OR operator ||
. name = name ?? "Bob";
is cleaner way to replace null
with a default value. b) I would not use !
in this code. Any time you use !
, you make it possible that a future maintainer of code might make a mistake, resulting in a rare runtime error: painful to debug. Safer is const name2:string = name ?? "Bob";
function postfix(...) { return name2.charAt(0) ...
–
Implement I solved this issue by editing the tsconfig.json file.
Under: "strict": true
,
add those 2 lines:
"noImplicitAny": false,
"strictNullChecks": false,
i had this same question a while back.. all types in ts are nullable, because void is a subtype of all types (unlike, for example, scala).
see if this flowchart helps - https://github.com/bcherny/language-types-comparison#typescript
void
being 'subtype of all types' (bottom type), refer to this thread. Also the chart you provided for scala is incorrect as well. Nothing
in scala is, in fact, the bottom type. Typescript, atm, does not have bottom type while scala does. –
Timoshenko Basing on previous answers, I'd like to add one more solution and explain it.
type Nullable<T> = T | null;
type SetAllNullable<T, ExceptKeys = void> = {
[P in keyof T]: P extends ExceptKeys
? T[P]
: Nullable<T[P]>
};
For example we have type Person
type Person = {
age: number;
name: string;
location: string;
}
and we want to set all fields to nullable
except name
. We can do it this way:
type PersonBrief = SetAllNullable<Person, 'name'>
As a result we'll have a new type where all fields except name
can have null
as value.
We can also provide a list of fields to be excluded from setting it to nullable
. The simple ways is
type PersonBriefOne = SetAllNullable<Person, 'name' | 'age'>;
and a bit more complex is
// `as const` makes this constant read-only and safe for making a type
const excludeFiledsList = ['name', 'age'] as const;
// this tricky string takes all array values by index and creates union type
type ExludedFieldsType = typeof excludeFiledsList[number];
type PersonBriefTwo = SetAllNullable<Person, ExludedFieldsType>;
So types PersonBriefOne
and PersonBriefTwo
will be similar.
One more note is that ExceptKeys
value is optional and can be skipped - in this case all fields will be set to nullable
, and even if non-existing key(s) will be provided as second argument, it will not cause any error and all fields will be set to nullable
as well.
Can define property as Partial
and Nullable
with type:
export type PartialNullable<T> = {
[P in keyof T]?: T[P] | null;
};
Them just use
interface Employee {
id: number;
company_id: number;
name: string;
age: number;
};
const EmployeeData: PartialNullable<Employee> = {
age: null,
name: null
};
You can define a Nullable
like type
like the following:
type NullOrUndefinedOr<T> = T extends void ? never : null | undefined | T;
This is an example of how it would look:
/* "fictitious" src/services/product.ts */
import { Product } from "@/models/product";
import { ProductRepository } from "@/repositories/product";
import { Logger } from "@/telemetry";
const log = Logger.create(__filename);
export async function getProductById(id: int): Promise<NullOrUndefinedOr<Product>> {
try{
const repo = await ProductRepository.create(process.env.DB_CONN_STRING);
return await repo.findOne({ productId: id });
}catch(err){
log.exception(err);
return null;
}
}
Note: You can also add this type to your
src/types/global.d.ts
to used it anywhere in your project without having to import it.
/* global.d.ts */
declare global {
type NullOrUndefinedOr<T> = T extends void ? never : null | undefined | T;
/** define all global types you need **/
}
/**
if you don't import or export anything else in this file
ensure to export at least an empty object to avoid the
compiler skipping this file
**/
export {};
© 2022 - 2024 — McMap. All rights reserved.
typescript@next
now.) – Expository