Type Transaction (#7581)

This one is interesting because we have transaction objects being passed around everywhere in the codebase but there's actually no Transaction class. It's a "mixin" that comes to life by being Object.assigned to the prototype of a real "class" (before class was cool!). Therefore, we can't just say `var Transaction = require('Transaction'); (transaction: Transaction) => { }` because it would be the object that contains a mixin and not an instance of a transaction.

The trick I use is to export `TransactionType` and alias it to `Transaction` in the file as it doesn't actually require transaction. In case they do, we'll figure it out, but in the few files I looked at, it doesn't seem to be the case.

For the perform function, it actually typechecks pretty well!
This commit is contained in:
Christopher Chedeau
2016-08-28 16:47:27 -07:00
committed by GitHub
parent dd0c65c6aa
commit a3e576e1bb
9 changed files with 37 additions and 35 deletions
@@ -175,7 +175,7 @@ var Mixin = {
};
Object.assign(ReactReconcileTransaction.prototype, Transaction.Mixin, Mixin);
Object.assign(ReactReconcileTransaction.prototype, Transaction, Mixin);
PooledClass.addPoolingTo(ReactReconcileTransaction);
@@ -88,7 +88,7 @@ var Mixin = {
Object.assign(
ReactServerRenderingTransaction.prototype,
Transaction.Mixin,
Transaction,
Mixin
);
@@ -13,9 +13,11 @@
'use strict';
var ReactUpdateQueue = require('ReactUpdateQueue');
var Transaction = require('Transaction');
var warning = require('warning');
import type { Transaction } from 'Transaction';
function warnNoop(publicInstance: ReactComponent<any, any, any>, callerName: string) {
if (__DEV__) {
var constructor = publicInstance.constructor;
@@ -39,7 +41,7 @@ function warnNoop(publicInstance: ReactComponent<any, any, any>, callerName: str
* @param {Transaction} transaction
*/
class ReactServerUpdateQueue {
/* :: transaction: Transaction; */
transaction: Transaction;
constructor(transaction: Transaction) {
this.transaction = transaction;
@@ -109,7 +109,7 @@ var Mixin = {
Object.assign(
ReactNativeReconcileTransaction.prototype,
Transaction.Mixin,
Transaction,
ReactNativeReconcileTransaction,
Mixin
);
@@ -36,7 +36,7 @@ function ReactDefaultBatchingStrategyTransaction() {
Object.assign(
ReactDefaultBatchingStrategyTransaction.prototype,
Transaction.Mixin,
Transaction,
{
getTransactionWrappers: function() {
return TRANSACTION_WRAPPERS;
@@ -75,7 +75,7 @@ function ReactUpdatesFlushTransaction() {
Object.assign(
ReactUpdatesFlushTransaction.prototype,
Transaction.Mixin,
Transaction,
{
getTransactionWrappers: function() {
return TRANSACTION_WRAPPERS;
@@ -92,7 +92,7 @@ Object.assign(
perform: function(method, scope, a) {
// Essentially calls `this.reconcileTransaction.perform(method, scope, a)`
// with this transaction's wrappers around it.
return Transaction.Mixin.perform.call(
return Transaction.perform.call(
this,
this.reconcileTransaction.perform,
this.reconcileTransaction,
+20 -20
View File
@@ -7,12 +7,15 @@
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule Transaction
* @flow
*/
'use strict';
var invariant = require('invariant');
var OBSERVED_ERROR = {};
/**
* `Transaction` creates a black box that is able to wrap any method such that
* certain invariants are maintained before and after the method is invoked
@@ -74,7 +77,7 @@ var invariant = require('invariant');
*
* @class Transaction
*/
var Mixin = {
var TransactionImpl = {
/**
* Sets up this instance so that it is prepared for collecting metrics. Does
* so such that this setup method may be used on an instance that is already
@@ -82,7 +85,7 @@ var Mixin = {
* That can be useful if you decide to make your subclass of this mixin a
* "PooledClass".
*/
reinitializeTransaction: function() {
reinitializeTransaction: function(): void {
this.transactionWrappers = this.getTransactionWrappers();
if (this.wrapperInitData) {
this.wrapperInitData.length = 0;
@@ -100,7 +103,7 @@ var Mixin = {
*/
getTransactionWrappers: null,
isInTransaction: function() {
isInTransaction: function(): bool {
return !!this._isInTransaction;
},
@@ -121,7 +124,13 @@ var Mixin = {
*
* @return {*} Return value from `method`.
*/
perform: function(method, scope, a, b, c, d, e, f) {
perform: function<
A, B, C, D, E, F, G,
T: (a: A, b: B, c: C, d: D, e: E, f: F) => G // eslint-disable-line space-before-function-paren
>(
method: T, scope: any,
a: A, b: B, c: C, d: D, e: E, f: F,
): G {
invariant(
!this.isInTransaction(),
'Transaction.perform(...): Cannot initialize a transaction when there ' +
@@ -160,7 +169,7 @@ var Mixin = {
return ret;
},
initializeAll: function(startIndex) {
initializeAll: function(startIndex: number): void {
var transactionWrappers = this.transactionWrappers;
for (var i = startIndex; i < transactionWrappers.length; i++) {
var wrapper = transactionWrappers[i];
@@ -169,12 +178,12 @@ var Mixin = {
// OBSERVED_ERROR state before overwriting it with the real return value
// of initialize -- if it's still set to OBSERVED_ERROR in the finally
// block, it means wrapper.initialize threw.
this.wrapperInitData[i] = Transaction.OBSERVED_ERROR;
this.wrapperInitData[i] = OBSERVED_ERROR;
this.wrapperInitData[i] = wrapper.initialize ?
wrapper.initialize.call(this) :
null;
} finally {
if (this.wrapperInitData[i] === Transaction.OBSERVED_ERROR) {
if (this.wrapperInitData[i] === OBSERVED_ERROR) {
// The initializer for wrapper i threw an error; initialize the
// remaining wrappers but silence any exceptions from them to ensure
// that the first error is the one to bubble up.
@@ -193,7 +202,7 @@ var Mixin = {
* (`close`rs that correspond to initializers that failed will not be
* invoked).
*/
closeAll: function(startIndex) {
closeAll: function(startIndex: number): void {
invariant(
this.isInTransaction(),
'Transaction.closeAll(): Cannot close transaction when none are open.'
@@ -209,7 +218,7 @@ var Mixin = {
// close -- if it's still set to true in the finally block, it means
// wrapper.close threw.
errorThrown = true;
if (initData !== Transaction.OBSERVED_ERROR && wrapper.close) {
if (initData !== OBSERVED_ERROR && wrapper.close) {
wrapper.close.call(this, initData);
}
errorThrown = false;
@@ -229,15 +238,6 @@ var Mixin = {
},
};
var Transaction = {
export type Transaction = typeof TransactionImpl;
Mixin: Mixin,
/**
* Token to look for to determine if an error occurred.
*/
OBSERVED_ERROR: {},
};
module.exports = Transaction;
module.exports = TransactionImpl;
@@ -45,7 +45,7 @@ describe('Transaction', function() {
this.secondCloseParam = INIT_ERRORED; // WILL be set to something else
this.lastCloseParam = INIT_ERRORED; // WON'T be set to something else
};
Object.assign(TestTransaction.prototype, Transaction.Mixin);
Object.assign(TestTransaction.prototype, Transaction);
TestTransaction.prototype.getTransactionWrappers = function() {
return [
{
@@ -96,7 +96,7 @@ describe('Transaction', function() {
this.secondCloseParam = INIT_ERRORED; // WILL be set to something else
this.lastCloseParam = INIT_ERRORED; // WILL be set to something else
};
Object.assign(TestTransaction.prototype, Transaction.Mixin);
Object.assign(TestTransaction.prototype, Transaction);
TestTransaction.prototype.getTransactionWrappers = function() {
return [
{
@@ -157,7 +157,7 @@ describe('Transaction', function() {
this.secondCloseParam = INIT_ERRORED; // WILL be set to something else
this.lastCloseParam = INIT_ERRORED; // WILL be set to something else
};
Object.assign(TestTransaction.prototype, Transaction.Mixin);
Object.assign(TestTransaction.prototype, Transaction);
// Now, none of the close/inits throw, but the operation we wrap will throw.
TestTransaction.prototype.getTransactionWrappers = function() {
return [
@@ -221,7 +221,7 @@ describe('Transaction', function() {
var TestTransaction = function() {
this.reinitializeTransaction();
};
Object.assign(TestTransaction.prototype, Transaction.Mixin);
Object.assign(TestTransaction.prototype, Transaction);
var exceptionMsg = 'This exception should throw.';
TestTransaction.prototype.getTransactionWrappers = function() {
return [
@@ -250,7 +250,7 @@ describe('Transaction', function() {
this.reinitializeTransaction();
this.firstCloseParam = INIT_ERRORED; // WILL be set to something else
};
Object.assign(TestTransaction.prototype, Transaction.Mixin);
Object.assign(TestTransaction.prototype, Transaction);
TestTransaction.prototype.getTransactionWrappers = function() {
return [
{
@@ -278,7 +278,7 @@ describe('Transaction', function() {
var NestedTransaction = function() {
this.reinitializeTransaction();
};
Object.assign(NestedTransaction.prototype, Transaction.Mixin);
Object.assign(NestedTransaction.prototype, Transaction);
NestedTransaction.prototype.getTransactionWrappers = function() {
return [
{
@@ -114,7 +114,7 @@ var Mixin = {
Object.assign(
ReactTestReconcileTransaction.prototype,
Transaction.Mixin,
Transaction,
ReactTestReconcileTransaction,
Mixin
);