简体   繁体   English

缩小到 TypeScript 中常见的 JS 方法和属性

[英]Narrowing down to common JS methods and properties in TypeScript

Is there a way to make this TypeScript narrowing less ugly (or more elegant ;-) ❓有没有办法让这个 TypeScript 缩小不那么难看(或更优雅;-) ❓

With IsSomething type guard, I want to narrow down access to common methods and properties of any JavaScript variable which is not null and not undefined (things like .toString , .valueOf , .hasOwnProperty , .constructor etc.)使用IsSomething类型保护,我想缩小对任何null且未undefined的 JavaScript 变量(如.toString.valueOf.hasOwnProperty.constructor等)的常用方法和属性的访问范围

Playground link 游乐场链接

export type Something = 
  number | string | boolean | symbol | object | bigint;

export function IsSomething(value: unknown) : value is Something {
  return value !== undefined && value !== null;
}

let a = [1, "2", 3];
Narrowing(a);
NotNarrowing(a);

function Narrowing(o: unknown) { // OK
    if (IsSomething(o)) {
      console.log(o.toString(), o.constructor);
    }
}

function NotNarrowing(o: unknown) { // NOT OK
    if (o !== null && o !== undefined) {
      console.log(o.toString(), o.constructor);
    }
}

In TypeScript, every value except for null and undefined is assignable to the so-called empty object type {} .在 TypeScript 中,除了nullundefined之外的每个值都可以分配给所谓的空对象类型{} So you can simplify your Something to just {} and your user-defined type guard function will work the same as before:因此,您可以将您的Something简化为{} ,并且您的用户定义类型保护函数将像以前一样工作:

export type Something = {};
function isSomething(value: unknown): value is Something {
  return value !== undefined && value !== null;
}
function narrowing(o: unknown) { // OK
  if (isSomething(o)) {
    console.log(o.toString(), o.constructor);
  }
}

Furthermore, TypeScript 4.8 will introduce improved support for narrowing unknown , after which your "NotNarrowing" function will start working, because checking (o !== null && o !== undefined) will narrow o from unknown to {} automatically:此外,TypeScript 4.8 将改进对缩小unknown的支持,之后您的“NotNarrowing”函数将开始工作,因为检查(o !== null && o !== undefined)会自动将ounknown缩小到{}

// TS4.8+ 
function nowNarrowing(o: unknown) { // okay
  if (o !== null && o !== undefined) {
    console.log(o.toString(), o.constructor);
  }
}

Playground link to code Playground 代码链接

@jcalz's excellent answer led me to the following solution: @jcalz 的出色回答使我找到了以下解决方案:

// With `IsSomething` type guard, I want to narrow down access 
// to common properties of any JS variable 
// which is not null and not undefined, like: 
// .toString, .valueOf, .hasOwnProperty, .constructor etc.

export function IsSomething(value: unknown) : value is Object {  
  return value !== null && value !== undefined;
}

Narrowing([1, 2, 3]);

function Narrowing(o: unknown) { // OK
    if (IsSomething(o)) {
      console.log(o.toString(), o.constructor);
    }
}

It turns out, TypeScript has a predefined interface Object , which works exactly the way I want:事实证明,TypeScript 有一个预定义的interface Object ,它完全按照我想要的方式工作:

TypeScript 中的对象接口

In TypeScript, the Object interface is different from the primitive object type, as the former is still assignable to any other primitive or complex type.在 TypeScript 中, Object接口不同于原始object类型,因为前者仍然可以分配给任何其他原始或复杂类型。 It is also different from an empty type like type Something = {} , as the latter doesn't supports IntelliSense.它也不同于type Something = {}之类的空类型,因为后者不支持 IntelliSense。

// Object interface is OK
let a: Object = 42; 
let b: Object = true;
let c: Object = "string";
let d: Object = Symbol(42);
let e: Object = new class SomeClass { someProp = 42 };

// Empty type is OK but no IntelliSense 
let a1: {} = 42; 

// Error: Type 'number' is not assignable to type 'object'
let a2: object = 42; 

Playground link . 游乐场链接

One subtle issue still remains.一个微妙的问题仍然存在。 As @CertainPerformance pointed out , the above won't work for an edge case like Object.create(null) , where an object is created which doesn't have any properties or methods, but isn't null either.正如@CertainPerformance 指出的那样,上述方法不适用于像Object.create(null)这样的边缘情况,其中创建的对象没有任何属性或方法,但也不为null

If that's a requirement, the following should do the job:如果这是一项要求,则应执行以下操作:

// can be extended to check for toString, valueOf, etc
export function IsSomething(value: unknown) : value is Object {
  return (<Object>value)?.constructor !== undefined;
}

Narrowing([1, 2, 3]);

function Narrowing(o: unknown) { // OK
  if (IsSomething(o)) {
    console.log(o.toString(), o.constructor);
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM