I have looked into this thread Get local IP address in Node.js, where I want to implement a piece of code:
import net from 'net';
const getNetworkIP = (callback: any) => {
var socket = net.createConnection(80, 'www.google.com');
socket.on('connect', function () {
const sa = socket.address();
callback(undefined, sa.address);
socket.end();
});
socket.on('error', function (e) {
callback(e, 'error');
});
};
I can not call sa.address
because the socket.address()
will return either {}
or net.AddressInfo
. How to deal with this common code scenario in typescript? How can I access the address
attribute safely?
CodePudding user response:
This is weird since the docs don't mention the possibility of returning an empty object.
However, blaming the @types/node
declaration files leads to this PR with the comment
- address(): AddressInfo; address(): AddressInfo | {};
In case of windows pipes or unix domain sockets
{}
is returned (server returns the path as string).
So if you're certain that this doesn't apply to your code, you can use a type assertion:
const sa = socket.address() as net.AddressInfo;
callback(undefined, sa.address);
but if you want to do it properly and detect the occurrence you'd write
const sa = socket.address();
if ('address' in sa) {
callback(undefined, sa.address);
} else {
callback(new Error(`Expected an AddressInfo object, got ${sa}`));
}
CodePudding user response:
You can validate the shape of the returned value at runtime with a type guard function. See comments below:
Ref: Class:
net.Socket
| Net | Node.js v18.12.0 Documentation
import {default as net, type AddressInfo, type Socket} from 'node:net';
// The type of your callback signature looks like it's one of these:
type NetworkIPCallback = {
/** For handling an error */
(error: Error, msg: 'error'): void;
/** For handling a discovered address */
(_error: undefined, address: string): void;
};
/** Type guard function */
function isAddressInfo (info: ReturnType<Socket['address']>): info is AddressInfo {
return (
typeof info === 'object'
&& typeof (info as AddressInfo)?.address === 'string'
&& typeof (info as AddressInfo)?.family === 'string'
&& typeof (info as AddressInfo)?.port === 'number'
);
}
const getNetworkIP = (callback: NetworkIPCallback) => {
const socket = net.createConnection(80, 'www.google.com');
socket.on('connect', () => {
const info = socket.address();
if (isAddressInfo(info)) callback(undefined, info.address);
else callback(new Error('Socket address not found'), 'error');
socket.end();
});
socket.on('error', (error) => callback(error, 'error'));
};
And if you only care about the object having an address
property of type string
, then you could use a simplified type guard:
function hasStringAddress <T>(value: T): value is T & { address: string } {
return (
typeof value === 'object'
&& typeof (value as any)?.address === 'string'
);
}