Coder Social home page Coder Social logo

not-a-hub's People

Contributors

fuafa avatar

Stargazers

 avatar  avatar

not-a-hub's Issues

Higher order function type inference #30215

// 在一个函数调用 F(args) 中,如果一个参数表达式 args 是一个范型函数 H,那么 H 中得类型参数 T 会传播到返回类型中,当且仅当:
//              |                            |               |                   |
//         pipe(list, box)              (list or box)  (list<T>, box<V>)       (T, V)
// 1. 当 F 是一个范型函数而且返回一个函数 G 只有一个 single call signature (怎么翻译。。。)
//       |                           |
//    pipe<A extends any[], B, C>   (...args: A) => C
// 2. G 本身并不会引入新的类型参数
//    |
//   (...args: A) => C 中 A, C 都来自 pipe
// 3. in the left-to-right processing of the function call arguments, 
//    no inferences have been made for any of the type parameters referenced in the contextual type for the argument expression.(啥意思?)
//    尝试解读:比如 pipe(list, box) 对于 list<T> 中的 T 没有发生任何的推导,只有 propagation.
declare function pipe<A extends any[], B, C>(ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C;

declare function list<T>(a: T): T[];
declare function box<V>(x: V): { value: V };

const listBox = pipe(list, box);  // <T>(a: T) => { value: T[] }
const boxList = pipe(box, list);  // <V>(x: V) => { value: V }[]

// 不能从右到左推导
const arrowBox = pipe(x => [x], box) // (x: any) => { value: any[] }

const x1 = listBox(42);  // { value: number[] }
const x2 = boxList("hello");  // { value: string }[]

wtf onChange

type EventHandler<T> = { bivarianceHack(data: T): void }['bivarianceHack']


interface FormProp<T> {
    onChange: EventHandler<T>
    onChange2: <R extends string>(data: R) => void
    onChange3<S extends string>(data: S): void
    onChange4(data: string): void
    onChange5: (data: string) => void
}

declare const Form: React.ComponentType<FormProp<string>>
declare const onChange: (data: '') => void

<Form
    onChange={onChange} // pass
    onChange2={onChange} // fail
    onChange3={onChange} // fail
    onChange4={onChange} // pass
    onChange5={onChange} //fail
/>

Assignability is not a transitive relation

在 TypeScript 中, 已知 A <: B, B :< C, 并不能推断出 A <: C, 例如

  1. number <: any, any <: string;
  2. {x: number, y: string} <: {x: number}, {x: number} <: {x: number, y?: string};
  3. number <: Object, Object <: object

Resolution of conditional type

Two kinds of conditional type

  1. 当类型参数位于 extends type (RHS of extends), 称之为 Non-distributive Conditional Types.
type NonDistributive<T> = string extends T ? true : false;
  1. 当类型参数位于 check type (LHS of extends), 称之为 Distributive Conditional Types.
type Distributive<T> = T extends string ? true : false;

Returns from resolution

  1. Resolved, 有足够信息可以推断出 true or false, 就可以 resolved 到其中一个分支. (有时候有足够信息也不行, Design limitation of TypeScript, 见下下).
type Resolved = Distributive<string> // resolved to true;
  1. Deferred, 没有足够信息可以推断出 true or false.
type Deferred<T extends string> = NonDistributive<T> // deferred.

Deferred even we have sufficient evidence to prove definitely true, or definitely false.

type StillDeferred<T extends string> = Distributive<T>; // deferred.

如果 T in StillDeferred <: string, 难道推不出来 T in Distributive <: string 吗?

Why: Conditional type resolution 会忽略 type parameter constraints.
Why Why: /master/src/compiler/checker.ts -> createTypeChecker -> getConditionalType, 大概 10705 行

// Return trueType for a definitely true extends check. We check instantiations of the two
// types with type parameters mapped to their restrictive form, i.e. a form of the type parameter
// that has no constraint. This ensures that, for example, the type
// type Foo<T extends { x: any }> = T extends { x: string } ? string : number
// doesn't immediately resolve to 'string' instead of being deferred.

TODO.

  1. definitely truedefinitely false 是什么意思.
  2. definitely assignable relation 是什么.

OOP versus Functional decomposition (draft)

Specification

  1. Implement three data variances: Int, Add, Negate.
  2. Implement three operations: evalExp, toString, hasZero on each data variance.
/** 
 * THE GRID
 *              evalExp      toString        hasZero      noNegConstants (to be extended)
 *
 * Int
 *
 * Add
 *
 * Negate
 *
 * Multiply (to be extended)
 * 
 */

Functional approach

  1. Break programs down into functions that perform some operation.
  2. Easy for adding new operations (column). For example adding noNegConstants, we just need to add a new function and keep the remaining unchanged.
  3. Difficult for adding new variance (row). For example adding Multiply, we would need to alter 4 blocks of code.
// 写成这样是想要有 pattern matching 的支持,
// 然而只能做到 exhaustive check, 而没有解构.
type Exp = 
  { kind: 'Int', value: Int } 
  | { kind: 'Negate', value: Negate}
  | { kind: 'Add', value: Add }
  // hard
  // | { kind: 'Multiply', value: Multiply} 

// ts 没有 type constructor, 所以需要另外给每一个 variance 新建一个 class.
// 在支持 type constructor 的语言中, 例如 ml 系, 可以写成:
// datatype Exp = Int of int | Negate of Exp | Add of Exp * Exp
class Int {
  v: number;
  constructor(v: number) {
    this.v = v;
  }
}

class Negate {
  v: Exp;
  constructor(v: Exp) {
    this.v = v;
  }
}

class Add {
  v1: Exp;
  v2: Exp;
  constructor(v1: Exp, v2: Exp) {
    this.v1 = v1;
    this.v2 = v2;
  }
}

// hard
// class Multiply {
//   e1: Exp;
//   e2: Exp;
//   constructor(e1: Exp, e2: Exp) {
//     this.e1 = e1;
//     this.e2 = e2;
//  }

function addValue(v1: Exp, v2: Exp): Exp {
  if (v1.kind !== 'Int' || v2.kind !== 'Int') {
    throw new Error('Non-numbers in addition')
  }
  return {
    kind: 'Int',
    value: new Int(v1.value.v + v2.value.v)
  }
}

function evalExp(e: Exp): Exp {
  switch(e.kind) {
    case 'Int':
      return e;
    case 'Negate':
      const e1 = evalExp(e.value.e);
      if (e1.kind === 'Int') {
        return {
          kind: 'Int',
          value: new Int(-e1.value.v)
        };
      }
      throw new Error('Non-numbers in negation');
    case 'Add':
      return addValue(
        evalExp(e.value.e1),
        evalExp(e.value.e2)
      );
    // hard
    // case 'Multiply':
    //   ...
  }
}

function toString(e: Exp): string {
  switch(e.kind) {
    case 'Int':
      return String(e.value.v);
    case 'Negate':
      return `-${toString(e.value.e)}`;
    case 'Add':
      return `${toString(e.value.e1)} + ${toString(e.value.e2)}`;
    // hard
    // case 'Multiply':
    //   ...
  }
}

function hasZero(e: Exp): boolean {
  switch(e.kind) {
    case 'Int':
      return e.value.v === 0;
    case 'Negate':
      return hasZero(e.value.e);
    case 'Add':
      return hasZero(e.value.e1) || hasZero(e.value.e2);
    // hard
    // case 'Multiply':
    //   ...
  }
}

// easy
// function noNegConstants(e: Exp): Exp {
//   switch(e.kind) {
//     case 'Int':
//     case 'Negate':
//     case 'Add':
//     // case 'Multiply'
//   }
// }

OOP

  1. Break programs down into classes that give behavior to some kind of data.
  2. Easy for adding new variance (row), for example adding Multiply.
  3. Difficult for adding new operations (column), for example adding noNegConstants.
interface Exp {
  evalExp(): Int;
  toString(): string;
  hasZero(): boolean;
}

class Int implements Exp {
  v: number;
  constructor(v: number) {
    this.v = v;
  }
  evalExp() {
    return this; 
  }
  toString() {
    return String(this.v);
  }
  hasZero() {
    return this.v === 0;
  }
}

class Negate implements Exp {
  e: Exp
  constructor(e: Exp) {
    this.e = e;
  }
  evalExp() {
    return new Int(this.e.evalExp().v);
  }
  toString() {
    return `-${this.e.toString()}`;
  }
  hasZero() {
    return this.e.hasZero();
  }
}

class Add implements Exp {
  e1: Exp;
  e2: Exp;
  constructor(e1: Exp, e2: Exp) {
    this.e1 = e1;
    this.e2 = e2;
  }

  evalExp() {
    return new Int(this.e1.evalExp().v + this.e2.evalExp().v);
  }

  toString() {
    return this.e1.toString() + this.e2.toString();
  }
  hasZero() {
    return this.e1.hasZero() || this.e2.hasZero();
  }
}

Specification 2

Suppose there are two more variances to be extended MyString and Rational, and the function add_values now work for any pairs of the variances but not just the Int. If one of the candidate is of type MyString, then we do the concatenation.

Binary methods with functional decomposition

Set is not a functor

# Any functor must have a `map` method:
map :: Functor f => f a ~> (a -> b) -> f b

Since there is no Setoid restriction on b, b may not be comparable, but Set only contains value which is comparable
dagaishizheyang

Render wrong list

Reproduce

  1. go to 我的 PR 不可能被 APPROVED
  2. click issue then quickly click pr
  3. If issue's request resolved later than pr's, list render issue feeds

在 JavaScript 的项目里享受 d.ts 类型提示

文件结构

project
|- src
   |- index.js
   |- package
     |- module.js
     |- module.d.ts

index.jsimport module

// module.js

export function foo(x) {
  return x;
}
// module.d.ts

export declare function foo(x: number): number;
// index.js

import { foo } from './src/module';

// foo 的类型是 number -> number
// 因为有一个同名的 d.ts 文件,TypeScript 可以找到相对应的类型,
// 但是生成 JS 文件中 `foo` 还是会正确的指向 module.js 中的 `foo`
foo

然而 module.js 并不能享受到 module.d.ts 中的类型提示

// module.js

// foo 的类型是 any -> any
export function foo(x) {
  return x;
}

因为 module.js 和 module.d.ts 是两个不同的 module(指 ES 中的 module), 他们有属于自己的作用域。

让 module.js 也享受 module.d.ts 的类型提示

目前只能通过在 js-doc 中使用动态 import 的语法,把 foo 联系起来。

// module.js

/**
 * 
 * @type {import("./module").foo}
 * 这里 foo 的类型是 number -> number
 */
export function foo(x) { return x; }

如果 module 里有多个方法,那么

/**
 * 
 * @typedef {import("./module")} Module  
 */

/**
 * 
 * @type {Module["foo"]}
 */
export function foo(x) {
    return {}
}

神 TM 展望未来

未来可能会引入新的 js-doc tag named @import and @from, 好处是看起比上面的 intuitive.

有两种 proposals:

  1. 难看,跟 ESModule syntax 不一样,但是有更强大的自动补全
/**
 * 
 * @from "./module"
 * @import { foo } from
 * @import Default
 * @import * as ns 
 */
  1. 补全没有 1 好,但是语法跟 ESModule syntax 一样
/**
 * @import { foo, y as z, default as Default } from "./module"
 */

Conclusion

这么麻烦,还不如直接写 TypeScript...

Reference:

microsoft/TypeScript#14342
microsoft/TypeScript#14377
microsoft/TypeScript#14844
microsoft/TypeScript#22160

Generic type narrowing by `typeof`

type IsString<X> = [X] extends [string] ? true : false

function isString<X extends string, Y>(x: X, y: Y)  {
    if (typeof x === "string") {
		// 因为 typeof x === 'string' 并没有为 X 增加额外的信息 (X 本来就 extends string)
		x; // X extends string,
        const a: IsString<typeof x> = true; // error: conditional type is deferred
    }
    if (typeof y === "string") {
		// 1. Y 原来并没有任何约束, (no extends)
		// 2. typeof y === 'string' -> typeof y <: string, 
		// 3. 同时 y: Y, 
		// 4. 所以 typeof y <: Y & string
		y; // Y & string,
        const b: IsString<typeof y> = true;
    }
}

microsoft/TypeScript#29939 (comment)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.