# typeName()

[JS](https://lochiwei.gitbook.io/web/js) ⟩ [value](https://lochiwei.gitbook.io/web/js/val) ⟩ [type](https://lochiwei.gitbook.io/web/js/val/type) ⟩ [name](https://lochiwei.gitbook.io/web/js/val/type/name) ⟩ typeName()

{% tabs %}
{% tab title="⬇️ 應用" %}

* [gas](https://lochiwei.gitbook.io/web/appendix/gas "mention") app [helpers](https://lochiwei.gitbook.io/web/appendix/gas/app/helpers "mention").
* [print](https://lochiwei.gitbook.io/web/js/val/obj/proto/chain/print "mention") - print the [chain](https://lochiwei.gitbook.io/web/js/val/obj/proto/chain "mention") of an [obj](https://lochiwei.gitbook.io/web/js/val/obj "mention").
  {% endtab %}

{% tab title="📃 結果" %}

* replit：[typeName() (v.2)](https://replit.com/@pegasusroe/typeName-from-MDN#index.js)

```javascript
typeof       base         type         expr         value       
--------------------------------------------------------------------------
object⭐️     Null         Null         null         null
--------------------------------------------------------------------------
undefined    Undefined    Undefined    undefined    undefined
--------------------------------------------------------------------------
number       Number       Number       37           37
number       Number       Number       3.14         3.14
number       Number       Number       Math.LN2     0.6931471805599453
number       Number       Number       Infinity     Infinity
number       Number       Number       NaN          NaN
number       Number       Number       Number('1')  1
number       Number       Number       Number('ab') NaN
--------------------------------------------------------------------------
bigint       BigInt       BigInt       42           42n
--------------------------------------------------------------------------
string       String       String       'bla'        bla
string       String       String       `x = ${1+2}` x = 3
string       String       String       typeof 1     number
string       String       String       String({})   [object Object]
string       String       String       typeof xxx   undefined
--------------------------------------------------------------------------
boolean      Boolean      Boolean      true         true
boolean      Boolean      Boolean      Boolean(1)   true
boolean      Boolean      Boolean      !!(1)        true
--------------------------------------------------------------------------
symbol       Symbol       Symbol       Symbol()     Symbol()
symbol       Symbol       Symbol       Symbol.iterator Symbol(Symbol.iterator)
--------------------------------------------------------------------------
object       Object       Object       {a:1}        { a: 1 }
object⭐️     User         User         user         User { name: 'JohnDoe' }
object⭐️     Array        Array        [1, 2]       [ 1, 2 ]
object⭐️     Date         Date         new Date()   2022-09-13T01:26:32.214Z
object⭐️     RegExp       RegExp       /regex/      /regex/
--------------------------------------------------------------------------
function     Function     Function     function(){} [Function (anonymous)]
function     Function     Function     Math.sin     [Function: sin]
function     Function     Function     () => {}     [Function (anonymous)]
function⭐️❗  Function     class        class {}     [class (anonymous)]
function⭐️❗  Function     class        User         [class User]
function⭐️   GeneratorFunction GeneratorFunction function*(){} [GeneratorFunction (anonymous)]
--------------------------------------------------------------------------
• ⭐️ types not all the same        • ❗ base !== type
```

{% endtab %}

{% tab title="💾 程式" %}

* replit：[typeName() (v.2)](https://replit.com/@pegasusroe/typeName-from-MDN#index.js)
* require： [isclass](https://lochiwei.gitbook.io/web/js/val/type/type-functions/isclass "mention"), [base-type-name](https://lochiwei.gitbook.io/web/js/val/type/name/base-type-name "mention")

```javascript
// ⭐️ type name
// - require: 
//   • baseTypeName()
//   • isClass()
function typeName(value) {
    if (isClass(value)) return "class";    // class
    return baseTypeName(value);            // other cases
}
```

📁 module (<mark style="color:yellow;">**copy and paste this file**</mark> to use it)

```javascript
// ⭐ base type name
function baseTypeName(value) {
    // --------------
    //          ↓   ↱ -1 (index of last character)
    //  0123456789012
    //          ╭──╮
    // "[object XXXX]"             ╭───╮
    //          ^^^^  <----  slice(8, -1)
    return Object.prototype.toString.call(value).slice(8, -1); 
}

/**    
 * ⭐️ content of function's definition
 *
 * @throws {TypeError} will throw a TypeError if value is not a function
 * @return {string}
 */
function functionDefinition(value) {
    
    // ---------------------------------------------------------------------
    // ⭐️ will throw a TypeError if value is not a function:
    //   ⛔ TypeError: 
    //      Function.prototype.toString requires that 'this' be a Function
    // ---------------------------------------------------------------------
    
    return Function.prototype.toString.call(value);
}

// ⭐️ check if value is a function
// - require: baseTypeName()
function isFunction(value) {
    return baseTypeName(value) === "Function";
}

// ⭐️ check if value is a "class"
// - require: 
//   • isFunction()
//   • functionDefinition()
function isClass(value) {
    return (
        // `value` is a function
        isFunction(value) &&
        // `value` is a "class" definition
        functionDefinition(value).startsWith("class")
    );
}

// ⭐️ type name
// - require: 
//   • baseTypeName()
//   • isClass()
function typeName(value) {
    if (isClass(value)) return "class";    // class
    return baseTypeName(value);            // other cases
}

// node exports
module.exports = { 
    baseTypeName,
    isFunction,
    functionDefinition,
    isClass,
    typeName, 
}
```

{% endtab %}

{% tab title="💈範例" %}

* replit：[typeName() (v.2)](https://replit.com/@pegasusroe/typeName-from-MDN#index.js)
* 📁 index.js

```javascript
// ⭐ import (functions for type name)
const { typeName, baseTypeName } = require('./typeNames.js');
const { testCases } = require('./testCases.js');

// table settings
const header = ['typeof', 'base', 'type', 'expr', 'value'];
const cols = header.length;
const [width, pad, ext] = [12, 1, 10];
const line = '-'.repeat(width*cols + pad*(cols-1) + ext);

// header
console.log(...header.map(s => s.padEnd(width, ' ')));
console.log(line);

// rows
for (const testCase of testCases) {

    // separator
    if (testCase === '---') {
        console.log(line); 
        continue; 
    }
    
    // test cases
    const value = testCase[0];
    const expr = testCase[1] || String(testCase[0]);

    const types = [typeof value, baseTypeName(value), typeName(value)];
    const numberOfDifferentTypes = new Set(types.map(s => s.toLowerCase())).size;

    if (numberOfDifferentTypes > 1) types[0] += '⭐️';
    if (baseTypeName(value) !== typeName(value)) types[0] += '❗';
    
    console.log(
        ...[...types, expr].map(s => s.padEnd(width, ' ')),
        value,
    )
}

console.log(line);

// legend
console.log(`• ⭐️ types not all the same        • ❗ base !== type`);
```

* 📁 testCases.js

```javascript
// custom class
class User {
    // ⭐ class field
    name = 'JohnDoe';
    // ⭐ custom class name
    get [Symbol.toStringTag]() { return 'User' }
}

// instance
let user = new User();

// test values: [expr, expr string]
const testCases = [  

    // ⭐️ null
    [null, ], 
    
    '---',

    // ⭐️ undefined --------------------------------------
    [undefined, ],

    '---',
    
    // ⭐️ Number --------------------------------------
    [37, ],
    [3.14, ],
    [Math.LN2, 'Math.LN2'],
    [Infinity, ],
    [NaN, ],
    [Number('1'), `Number('1')`],        // 1
    [Number('abc'), `Number('ab')`],     // NaN❗

    '---',

    // ⭐️ bigInt --------------------------------------
    [42n, ], 

    '---',

    // ⭐️ String --------------------------------------
    ['bla', `'bla'`],
    [`x = ${1+2}`, '`x = ${1+2}`'],   // 
    [typeof 1, 'typeof 1'],           // always returns a string
    [String({}), 'String({})'],       // [object]❗
    [typeof xxx, 'typeof xxx'],       // 'undefined'❗ (string)

    '---',

    // ⭐️ Boolean --------------------------------------
    [true, ],
    [Boolean(1), 'Boolean(1)'],    // true
    [!!(1), '!!(1)'],              // true

    '---',

    // ⭐️ Symbol --------------------------------------
    [Symbol(), ],
    [Symbol.iterator, 'Symbol.iterator'],

    '---',

    // ⭐️ objects --------------------------------------
    [{ a: 1 }, '{a:1}'],           // object literal
    [user, 'user'],                // instance of User
    
    [[1, 2], '[1, 2]'],            // Array
    [new Date(), 'new Date()'],    // Date
    [/regex/, ],                   // RegExp
    
    '---',

    // ⭐️ Functions --------------------------------------
    [function(){}, 'function(){}'],   // "normal" function
    [Math.sin, 'Math.sin'],           // method
    [() => {}, '() => {}'],           // arrow function
    
    [class {}, 'class {}'],           // class ⭐️
    [User, 'User'],                   // class ⭐️

    [function*(){}, 'function*(){}'], // generator functions

];

// export
module.exports = { User, testCases };
```

{% endtab %}

{% tab title="📘 手冊" %}

* [ ] [typeof](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof) ⟩&#x20;
  * [ ] [custom method that gets a more specific type](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof#custom_method_that_gets_a_more_specific_type) ⭐️
  * [ ] [real-world usage](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof#real-world_usage)
* [ ] [Object.prototype.toString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString)
* [ ] [Symbol.toStringTag](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toStringTag) ⭐️
* [ ] [instanceof](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof)
  {% endtab %}

{% tab title="🧨 雷區" %}
{% hint style="danger" %}
objects can <mark style="color:red;">**change**</mark> the behavior of <mark style="color:yellow;">`Object.prototype.toString()`</mark> by defining a [`Symbol.toStringTag`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toStringTag) property, leading to <mark style="color:red;">**unexpected results**</mark>:
{% endhint %}

```javascript
const date = new Date();
date[Symbol.toStringTag] = 'XXX';
Date.prototype[Symbol.toStringTag] = 'prototype polluted';

const toString = Object.prototype.toString;    // keep it short
toString.call(date);           // [object XXX]
toString.call(new Date());     // [object prototype polluted]
```

{% endtab %}

{% tab title="👥 相關" %}

* typeName() is a <mark style="color:yellow;">**refined**</mark> version of [typeof](https://lochiwei.gitbook.io/web/js/val/type/name/typeof "mention").
* [isclass](https://lochiwei.gitbook.io/web/js/val/type/type-functions/isclass "mention") - distinguish [class](https://lochiwei.gitbook.io/web/js/val/class "mention") from [func](https://lochiwei.gitbook.io/web/js/val/func "mention").
  {% endtab %}
  {% endtabs %}
